oip-common 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,14 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { Injectable, inject, InjectionToken, signal, computed, effect, DestroyRef, ChangeDetectorRef, Component, Input, PLATFORM_ID, HostBinding, EventEmitter, Output, ViewChild, Renderer2, SecurityContext, ChangeDetectionStrategy, makeEnvironmentProviders, Pipe } from '@angular/core';
3
3
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
- import * as i2$6 from 'primeng/api';
4
+ import * as i2$3 from 'primeng/api';
5
5
  import { MessageService, ConfirmationService, PrimeIcons, SharedModule } from 'primeng/api';
6
6
  import { HttpErrorResponse, HttpClient as HttpClient$1, HttpHeaders } from '@angular/common/http';
7
7
  import * as i2$4 from '@ngx-translate/core';
8
8
  import { TranslateService, TranslatePipe, TranslateModule } from '@ngx-translate/core';
9
9
  import * as i1$3 from '@angular/router';
10
10
  import { ActivatedRoute, Router, RouterModule, NavigationEnd, RouterLinkActive, RouterLink } from '@angular/router';
11
- import { lastValueFrom, BehaviorSubject, Subject, ReplaySubject, merge, from, filter as filter$1, combineLatest, of, map as map$1, Observable } from 'rxjs';
11
+ import { lastValueFrom, BehaviorSubject, Subject, of, tap, shareReplay, ReplaySubject, merge, from, finalize, firstValueFrom, filter as filter$1, combineLatest, map as map$1, Observable } from 'rxjs';
12
12
  import { filter, distinctUntilChanged, map, switchMap, catchError } from 'rxjs/operators';
13
13
  import { Title, DomSanitizer } from '@angular/platform-browser';
14
14
  import { PrimeNG } from 'primeng/config';
@@ -36,15 +36,15 @@ import * as i2$2 from 'primeng/avatar';
36
36
  import { AvatarModule } from 'primeng/avatar';
37
37
  import * as i1$4 from 'primeng/contextmenu';
38
38
  import { ContextMenuModule, ContextMenu } from 'primeng/contextmenu';
39
- import * as i2$3 from 'primeng/dialog';
39
+ import * as i3$3 from 'primeng/dialog';
40
40
  import { DialogModule } from 'primeng/dialog';
41
- import * as i3$3 from 'primeng/inputtext';
41
+ import * as i4 from 'primeng/inputtext';
42
42
  import { InputTextModule, InputText } from 'primeng/inputtext';
43
43
  import { trigger, state, transition, style, animate } from '@angular/animations';
44
44
  import * as i3$2 from 'primeng/ripple';
45
45
  import { RippleModule } from 'primeng/ripple';
46
46
  import { ConfirmDialog } from 'primeng/confirmdialog';
47
- import * as i4 from 'primeng/select';
47
+ import * as i5 from 'primeng/select';
48
48
  import { SelectModule, Select } from 'primeng/select';
49
49
  import * as i1$5 from 'primeng/fileupload';
50
50
  import { FileUploadModule } from 'primeng/fileupload';
@@ -58,7 +58,7 @@ import { TagModule, Tag } from 'primeng/tag';
58
58
  import { TextareaModule, Textarea } from 'primeng/textarea';
59
59
  import * as i4$1 from 'primeng/toolbar';
60
60
  import { ToolbarModule } from 'primeng/toolbar';
61
- import * as i5 from 'primeng/card';
61
+ import * as i5$1 from 'primeng/card';
62
62
  import { CardModule } from 'primeng/card';
63
63
  import { DividerModule } from 'primeng/divider';
64
64
  import * as i6 from 'primeng/panel';
@@ -611,6 +611,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
611
611
  class L10nService {
612
612
  constructor() {
613
613
  this.loadedTranslations = new Set();
614
+ this.loadingTranslations = new Map();
614
615
  this.httpClient = inject(HttpClient$1);
615
616
  this.translateService = inject(TranslateService);
616
617
  this.primeNg = inject(PrimeNG);
@@ -621,8 +622,8 @@ class L10nService {
621
622
  * @param component - Name of the component to load translations for
622
623
  */
623
624
  loadComponentTranslations(component) {
624
- const lang = this.translateService.currentLang;
625
- this.loadTranslations(component, lang);
625
+ const lang = this.translateService.currentLang || this.layoutService.language() || 'en';
626
+ return this.loadTranslations(component, lang);
626
627
  }
627
628
  /**
628
629
  * Gets the translated value of a key (or an array of keys)
@@ -640,19 +641,27 @@ class L10nService {
640
641
  loadTranslations(component, lang) {
641
642
  const key = `${component}.${lang}`;
642
643
  if (this.loadedTranslations.has(key)) {
643
- return;
644
- }
645
- try {
646
- this.httpClient.get(`./assets/i18n/${component}.${lang}.json`).subscribe((translations) => {
647
- const current = this.translateService.translations[lang] || {};
648
- this.translateService.setTranslation(lang, { ...current, ...translations }, true);
649
- this.loadedTranslations.add(key);
650
- });
644
+ return of(null);
651
645
  }
652
- catch (e) {
653
- console.error(`No translations found for ${component}.${lang}.json`);
654
- console.error(e);
646
+ const loading = this.loadingTranslations.get(key);
647
+ if (loading) {
648
+ return loading;
655
649
  }
650
+ const request = this.httpClient.get(`./assets/i18n/${component}.${lang}.json`).pipe(tap((translations) => {
651
+ const current = this.translateService.translations[lang] || {};
652
+ this.translateService.setTranslation(lang, { ...current, ...translations }, true);
653
+ this.loadedTranslations.add(key);
654
+ this.loadingTranslations.delete(key);
655
+ }), shareReplay(1));
656
+ this.loadingTranslations.set(key, request);
657
+ request.subscribe({
658
+ error: (e) => {
659
+ this.loadingTranslations.delete(key);
660
+ console.error(`No translations found for ${component}.${lang}.json`);
661
+ console.error(e);
662
+ }
663
+ });
664
+ return request;
656
665
  }
657
666
  /**
658
667
  * Changes the lang currently used
@@ -717,6 +726,11 @@ class KeycloakSecurityService extends OidcSecurityService {
717
726
  */
718
727
  constructor() {
719
728
  super();
729
+ this.refreshLockKeyPrefix = 'oip:keycloak-refresh-lock';
730
+ this.refreshResultKeyPrefix = 'oip:keycloak-refresh-result';
731
+ this.refreshLockTtlMs = 15000;
732
+ this.refreshWaitTimeoutMs = 10000;
733
+ this.refreshTabId = this.createRefreshTabId();
720
734
  /**
721
735
  * Handles angular OIDC events.
722
736
  */
@@ -759,6 +773,14 @@ class KeycloakSecurityService extends OidcSecurityService {
759
773
  getAccessToken(configId) {
760
774
  return merge(super.getAccessToken(configId), this.accessToken.asObservable()).pipe(distinctUntilChanged());
761
775
  }
776
+ forceRefreshSession(customParams, configId) {
777
+ if (!this.refreshSession$) {
778
+ this.refreshSession$ = from(this.runSynchronizedRefresh(customParams, configId)).pipe(finalize(() => {
779
+ this.refreshSession$ = undefined;
780
+ }), shareReplay({ bufferSize: 1, refCount: false }));
781
+ }
782
+ return this.refreshSession$;
783
+ }
762
784
  /**
763
785
  * Indicates whether the current user has the 'admin' role.
764
786
  *
@@ -807,6 +829,174 @@ class KeycloakSecurityService extends OidcSecurityService {
807
829
  return payload.exp < Math.floor(Date.now() / 1000);
808
830
  }));
809
831
  }
832
+ async runSynchronizedRefresh(customParams, configId) {
833
+ const webLocks = this.getWebLocks();
834
+ if (webLocks) {
835
+ return webLocks.request(this.getRefreshLockKey(configId), { mode: 'exclusive' }, async () => {
836
+ const currentState = await this.syncAuthState(configId);
837
+ if (!(await this.isCurrentAccessTokenExpired())) {
838
+ return currentState;
839
+ }
840
+ return this.refreshAsLockOwner(customParams, configId);
841
+ });
842
+ }
843
+ return this.runSynchronizedRefreshWithStorageLock(customParams, configId);
844
+ }
845
+ async runSynchronizedRefreshWithStorageLock(customParams, configId) {
846
+ const startedAt = Date.now();
847
+ if (this.tryAcquireRefreshLock(configId)) {
848
+ try {
849
+ const currentState = await this.syncAuthState(configId);
850
+ if (!(await this.isCurrentAccessTokenExpired())) {
851
+ return currentState;
852
+ }
853
+ return await this.refreshAsLockOwner(customParams, configId);
854
+ }
855
+ finally {
856
+ this.releaseRefreshLock(configId);
857
+ }
858
+ }
859
+ const waitResult = await this.waitForRefreshResult(startedAt, configId);
860
+ if (waitResult === 'success') {
861
+ return this.syncAuthState(configId);
862
+ }
863
+ if (waitResult === 'error') {
864
+ throw new Error('Token refresh failed in another tab.');
865
+ }
866
+ return this.runSynchronizedRefreshWithStorageLock(customParams, configId);
867
+ }
868
+ async refreshAsLockOwner(customParams, configId) {
869
+ try {
870
+ const response = await firstValueFrom(super.forceRefreshSession(customParams, configId));
871
+ await this.applyLoginResponse(response, configId);
872
+ this.publishRefreshResult('success', configId);
873
+ return response;
874
+ }
875
+ catch (error) {
876
+ this.publishRefreshResult('error', configId);
877
+ throw error;
878
+ }
879
+ }
880
+ async syncAuthState(configId) {
881
+ const response = await firstValueFrom(super.checkAuth(undefined, configId));
882
+ await this.applyLoginResponse(response, configId);
883
+ return response;
884
+ }
885
+ async applyLoginResponse(response, configId) {
886
+ this.loginResponse.next(response);
887
+ this.currentUser.next(response.userData);
888
+ const token = await firstValueFrom(super.getAccessToken(configId));
889
+ this.accessToken.next(token);
890
+ const payload = await firstValueFrom(this.getPayloadFromAccessToken());
891
+ this.payload.next(payload);
892
+ }
893
+ async isCurrentAccessTokenExpired() {
894
+ const payload = await firstValueFrom(this.getPayloadFromAccessToken());
895
+ return !payload?.exp || payload.exp < Math.floor(Date.now() / 1000);
896
+ }
897
+ tryAcquireRefreshLock(configId) {
898
+ const lockKey = this.getRefreshLockKey(configId);
899
+ const now = Date.now();
900
+ const currentLock = this.readRefreshLock(lockKey);
901
+ if (currentLock && currentLock.ownerId !== this.refreshTabId && currentLock.expiresAt > now) {
902
+ return false;
903
+ }
904
+ const nextLock = {
905
+ ownerId: this.refreshTabId,
906
+ expiresAt: now + this.refreshLockTtlMs
907
+ };
908
+ localStorage.setItem(lockKey, JSON.stringify(nextLock));
909
+ return this.readRefreshLock(lockKey)?.ownerId === this.refreshTabId;
910
+ }
911
+ releaseRefreshLock(configId) {
912
+ const lockKey = this.getRefreshLockKey(configId);
913
+ const currentLock = this.readRefreshLock(lockKey);
914
+ if (currentLock?.ownerId === this.refreshTabId) {
915
+ localStorage.removeItem(lockKey);
916
+ }
917
+ }
918
+ waitForRefreshResult(startedAt, configId) {
919
+ const resultKey = this.getRefreshResultKey(configId);
920
+ return new Promise((resolve) => {
921
+ const timeoutId = window.setTimeout(() => {
922
+ cleanup();
923
+ resolve('timeout');
924
+ }, this.refreshWaitTimeoutMs);
925
+ const intervalId = window.setInterval(() => {
926
+ const result = this.tryResolveRefreshResult(resultKey, startedAt, configId);
927
+ if (result) {
928
+ cleanup();
929
+ resolve(result);
930
+ }
931
+ }, 250);
932
+ const onStorage = (event) => {
933
+ if (event.key === resultKey) {
934
+ const result = this.tryResolveRefreshResult(resultKey, startedAt, configId);
935
+ if (result) {
936
+ cleanup();
937
+ resolve(result);
938
+ }
939
+ }
940
+ };
941
+ const cleanup = () => {
942
+ window.clearTimeout(timeoutId);
943
+ window.clearInterval(intervalId);
944
+ window.removeEventListener('storage', onStorage);
945
+ };
946
+ window.addEventListener('storage', onStorage);
947
+ const result = this.tryResolveRefreshResult(resultKey, startedAt, configId);
948
+ if (result) {
949
+ cleanup();
950
+ resolve(result);
951
+ }
952
+ });
953
+ }
954
+ tryResolveRefreshResult(resultKey, startedAt, configId) {
955
+ const result = this.readRefreshResult(resultKey);
956
+ if (!result || result.timestamp < startedAt || result.configId !== configId) {
957
+ return null;
958
+ }
959
+ return result.status;
960
+ }
961
+ publishRefreshResult(status, configId) {
962
+ const result = {
963
+ configId,
964
+ ownerId: this.refreshTabId,
965
+ status,
966
+ timestamp: Date.now()
967
+ };
968
+ localStorage.setItem(this.getRefreshResultKey(configId), JSON.stringify(result));
969
+ }
970
+ readRefreshLock(lockKey) {
971
+ try {
972
+ const value = localStorage.getItem(lockKey);
973
+ return value ? JSON.parse(value) : null;
974
+ }
975
+ catch {
976
+ return null;
977
+ }
978
+ }
979
+ readRefreshResult(resultKey) {
980
+ try {
981
+ const value = localStorage.getItem(resultKey);
982
+ return value ? JSON.parse(value) : null;
983
+ }
984
+ catch {
985
+ return null;
986
+ }
987
+ }
988
+ getRefreshLockKey(configId) {
989
+ return `${this.refreshLockKeyPrefix}:${configId ?? 'default'}`;
990
+ }
991
+ getRefreshResultKey(configId) {
992
+ return `${this.refreshResultKeyPrefix}:${configId ?? 'default'}`;
993
+ }
994
+ getWebLocks() {
995
+ return navigator.locks ?? null;
996
+ }
997
+ createRefreshTabId() {
998
+ return window.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`;
999
+ }
810
1000
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: KeycloakSecurityService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
811
1001
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: KeycloakSecurityService }); }
812
1002
  }
@@ -2471,16 +2661,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
2471
2661
  class MenuApi extends HttpClient {
2472
2662
  constructor() {
2473
2663
  super(...arguments);
2474
- /**
2475
- * @description Retrieves the menu available to the current authenticated user.
2476
- *
2477
- * @tags Menu
2478
- * @name get
2479
- * @summary Retrieves the menu available to the current authenticated user.
2480
- * @request GET:/api/menu/get
2481
- * @secure
2482
- * @response `200` `(ModuleInstanceDto)[]` OK
2483
- */
2484
2664
  this.get = (params = {}) => this.request({
2485
2665
  path: `/api/menu/get`,
2486
2666
  method: "GET",
@@ -2488,16 +2668,6 @@ class MenuApi extends HttpClient {
2488
2668
  format: "json",
2489
2669
  ...params,
2490
2670
  });
2491
- /**
2492
- * @description Retrieves the admin-specific menu.
2493
- *
2494
- * @tags Menu
2495
- * @name getAdminMenu
2496
- * @summary Retrieves the admin-specific menu.
2497
- * @request GET:/api/menu/get-admin-menu
2498
- * @secure
2499
- * @response `200` `(ModuleInstanceDto)[]` OK
2500
- */
2501
2671
  this.getAdminMenu = (params = {}) => this.request({
2502
2672
  path: `/api/menu/get-admin-menu`,
2503
2673
  method: "GET",
@@ -2505,16 +2675,6 @@ class MenuApi extends HttpClient {
2505
2675
  format: "json",
2506
2676
  ...params,
2507
2677
  });
2508
- /**
2509
- * @description Retrieves all available modules in the system.
2510
- *
2511
- * @tags Menu
2512
- * @name getModules
2513
- * @summary Retrieves all available modules in the system.
2514
- * @request GET:/api/menu/get-modules
2515
- * @secure
2516
- * @response `200` `(IntKeyValueDto)[]` OK
2517
- */
2518
2678
  this.getModules = (params = {}) => this.request({
2519
2679
  path: `/api/menu/get-modules`,
2520
2680
  method: "GET",
@@ -2522,17 +2682,6 @@ class MenuApi extends HttpClient {
2522
2682
  format: "json",
2523
2683
  ...params,
2524
2684
  });
2525
- /**
2526
- * @description Adds a new module instance to the system.
2527
- *
2528
- * @tags Menu
2529
- * @name addModuleInstance
2530
- * @summary Adds a new module instance to the system.
2531
- * @request POST:/api/menu/add-module-instance
2532
- * @secure
2533
- * @response `200` `void` OK
2534
- * @response `500` `ApiExceptionResponse` Internal Server Error
2535
- */
2536
2685
  this.addModuleInstance = (data, params = {}) => this.request({
2537
2686
  path: `/api/menu/add-module-instance`,
2538
2687
  method: "POST",
@@ -2541,17 +2690,6 @@ class MenuApi extends HttpClient {
2541
2690
  type: ContentType.Json,
2542
2691
  ...params,
2543
2692
  });
2544
- /**
2545
- * @description Edits an existing module instance.
2546
- *
2547
- * @tags Menu
2548
- * @name editModuleInstance
2549
- * @summary Edits an existing module instance.
2550
- * @request POST:/api/menu/edit-module-instance
2551
- * @secure
2552
- * @response `200` `void` OK
2553
- * @response `500` `ApiExceptionResponse` Internal Server Error
2554
- */
2555
2693
  this.editModuleInstance = (data, params = {}) => this.request({
2556
2694
  path: `/api/menu/edit-module-instance`,
2557
2695
  method: "POST",
@@ -2560,17 +2698,6 @@ class MenuApi extends HttpClient {
2560
2698
  type: ContentType.Json,
2561
2699
  ...params,
2562
2700
  });
2563
- /**
2564
- * @description Deletes a module instance by its identifier.
2565
- *
2566
- * @tags Menu
2567
- * @name deleteModuleInstance
2568
- * @summary Deletes a module instance by its identifier.
2569
- * @request DELETE:/api/menu/delete-module-instance
2570
- * @secure
2571
- * @response `200` `void` OK
2572
- * @response `500` `ApiExceptionResponse` Internal Server Error
2573
- */
2574
2701
  this.deleteModuleInstance = (query, params = {}) => this.request({
2575
2702
  path: `/api/menu/delete-module-instance`,
2576
2703
  method: "DELETE",
@@ -2578,17 +2705,6 @@ class MenuApi extends HttpClient {
2578
2705
  secure: true,
2579
2706
  ...params,
2580
2707
  });
2581
- /**
2582
- * @description Swaps the order positions of two modules in the menu structure.
2583
- *
2584
- * @tags Menu
2585
- * @name changeOrder
2586
- * @summary Swaps the order positions of two modules in the menu structure.
2587
- * @request POST:/api/menu/change-order
2588
- * @secure
2589
- * @response `200` `void` OK
2590
- * @response `500` `ApiExceptionResponse` Internal Server Error
2591
- */
2592
2708
  this.changeOrder = (query, params = {}) => this.request({
2593
2709
  path: `/api/menu/change-order`,
2594
2710
  method: "POST",
@@ -3076,9 +3192,18 @@ class MenuItemCreateDialogComponent {
3076
3192
  constructor() {
3077
3193
  this.menuService = inject(MenuService);
3078
3194
  this.menu = inject(MenuApi);
3195
+ this.msgService = inject(MsgService);
3079
3196
  this.visibleChange = new EventEmitter();
3080
3197
  this.modules = [];
3198
+ this.iconOptions = Object.values(PrimeIcons)
3199
+ .filter((icon) => typeof icon === 'string')
3200
+ .map((icon) => ({
3201
+ label: icon.replace('pi pi-', ''),
3202
+ value: icon
3203
+ }))
3204
+ .sort((left, right) => left.label.localeCompare(right.label));
3081
3205
  this.selectIcon = 'pi pi-box';
3206
+ this.saving = false;
3082
3207
  }
3083
3208
  async ngOnInit() {
3084
3209
  this.modules = await this.menu.getModules();
@@ -3088,15 +3213,27 @@ class MenuItemCreateDialogComponent {
3088
3213
  this.visibleChange.emit(this.visible);
3089
3214
  }
3090
3215
  async save() {
3216
+ if (this.saving) {
3217
+ return;
3218
+ }
3091
3219
  const item = {
3092
3220
  moduleId: this.selectModule,
3093
3221
  label: this.label,
3094
3222
  icon: this.selectIcon,
3095
3223
  parentId: this.menuService.contextMenuItem?.moduleInstanceId
3096
3224
  };
3097
- await this.menuService.addModuleInstance(item);
3098
- await this.menuService.loadMenu();
3099
- this.hide();
3225
+ this.saving = true;
3226
+ try {
3227
+ await this.menuService.addModuleInstance(item);
3228
+ await this.menuService.loadMenu();
3229
+ this.hide();
3230
+ }
3231
+ catch (error) {
3232
+ this.msgService.error(error);
3233
+ }
3234
+ finally {
3235
+ this.saving = false;
3236
+ }
3100
3237
  }
3101
3238
  hide() {
3102
3239
  this.visible = false;
@@ -3112,7 +3249,8 @@ class MenuItemCreateDialogComponent {
3112
3249
  header="{{ 'menuItemCreateDialogComponent.header' | translate }}"
3113
3250
  [modal]="true"
3114
3251
  [style]="{ width: '40rem' }"
3115
- [(visible)]="visible">
3252
+ [(visible)]="visible"
3253
+ (keydown.enter)="save()">
3116
3254
  @if (menuService.contextMenuItem) {
3117
3255
  <div class="flex items-center gap-4 mb-4 mt-1">
3118
3256
  <label class="font-semibold w-1/3" for="oip-menu-item-create-dialog-parent-input">
@@ -3151,24 +3289,47 @@ class MenuItemCreateDialogComponent {
3151
3289
  <label class="font-semibold w-1/3" for="oip-menu-item-create-dialog-icon">
3152
3290
  {{ 'menuItemCreateDialogComponent.icon' | translate }}
3153
3291
  </label>
3154
- <i class="{{ selectIcon }}"></i>
3155
- <input class="flex-auto" id="oip-menu-item-create-dialog-icon" pInputText [(ngModel)]="selectIcon" />
3292
+ <p-select
3293
+ appendTo="body"
3294
+ class="flex-auto"
3295
+ filterBy="label,value"
3296
+ id="oip-menu-item-create-dialog-icon"
3297
+ optionLabel="label"
3298
+ optionValue="value"
3299
+ scrollHeight="18rem"
3300
+ [filter]="true"
3301
+ [options]="iconOptions"
3302
+ [(ngModel)]="selectIcon">
3303
+ <ng-template let-icon pTemplate="selectedItem">
3304
+ <div class="flex items-center gap-2">
3305
+ <i [class]="icon.value"></i>
3306
+ <span>{{ icon.label }}</span>
3307
+ </div>
3308
+ </ng-template>
3309
+ <ng-template let-icon pTemplate="item">
3310
+ <div class="flex items-center gap-2">
3311
+ <i [class]="icon.value"></i>
3312
+ <span>{{ icon.label }}</span>
3313
+ </div>
3314
+ </ng-template>
3315
+ </p-select>
3156
3316
  </div>
3157
3317
  <div class="flex justify-end gap-2">
3158
3318
  <p-button
3159
3319
  id="oip-menu-item-create-cancel"
3160
3320
  label="{{ 'menuItemCreateDialogComponent.cancel' | translate }}"
3161
3321
  severity="secondary"
3162
- (click)="changeVisible()"
3163
- (keydown)="changeVisible()" />
3322
+ [disabled]="saving"
3323
+ (click)="changeVisible()" />
3164
3324
  <p-button
3165
3325
  id="oip-menu-item-create-save"
3166
3326
  label="{{ 'menuItemCreateDialogComponent.save' | translate }}"
3167
- (click)="save()"
3168
- (keydown)="save()" />
3327
+ [disabled]="saving"
3328
+ [loading]="saving"
3329
+ (click)="save()" />
3169
3330
  </div>
3170
3331
  </p-dialog>
3171
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i2$3.Dialog, selector: "p-dialog", inputs: ["hostName", "header", "draggable", "resizable", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "appendTo", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3$3.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i4.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
3332
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i2$3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i3$3.Dialog, selector: "p-dialog", inputs: ["hostName", "header", "draggable", "resizable", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "appendTo", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i5.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
3172
3333
  }
3173
3334
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MenuItemCreateDialogComponent, decorators: [{
3174
3335
  type: Component,
@@ -3181,7 +3342,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
3181
3342
  header="{{ 'menuItemCreateDialogComponent.header' | translate }}"
3182
3343
  [modal]="true"
3183
3344
  [style]="{ width: '40rem' }"
3184
- [(visible)]="visible">
3345
+ [(visible)]="visible"
3346
+ (keydown.enter)="save()">
3185
3347
  @if (menuService.contextMenuItem) {
3186
3348
  <div class="flex items-center gap-4 mb-4 mt-1">
3187
3349
  <label class="font-semibold w-1/3" for="oip-menu-item-create-dialog-parent-input">
@@ -3220,21 +3382,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
3220
3382
  <label class="font-semibold w-1/3" for="oip-menu-item-create-dialog-icon">
3221
3383
  {{ 'menuItemCreateDialogComponent.icon' | translate }}
3222
3384
  </label>
3223
- <i class="{{ selectIcon }}"></i>
3224
- <input class="flex-auto" id="oip-menu-item-create-dialog-icon" pInputText [(ngModel)]="selectIcon" />
3385
+ <p-select
3386
+ appendTo="body"
3387
+ class="flex-auto"
3388
+ filterBy="label,value"
3389
+ id="oip-menu-item-create-dialog-icon"
3390
+ optionLabel="label"
3391
+ optionValue="value"
3392
+ scrollHeight="18rem"
3393
+ [filter]="true"
3394
+ [options]="iconOptions"
3395
+ [(ngModel)]="selectIcon">
3396
+ <ng-template let-icon pTemplate="selectedItem">
3397
+ <div class="flex items-center gap-2">
3398
+ <i [class]="icon.value"></i>
3399
+ <span>{{ icon.label }}</span>
3400
+ </div>
3401
+ </ng-template>
3402
+ <ng-template let-icon pTemplate="item">
3403
+ <div class="flex items-center gap-2">
3404
+ <i [class]="icon.value"></i>
3405
+ <span>{{ icon.label }}</span>
3406
+ </div>
3407
+ </ng-template>
3408
+ </p-select>
3225
3409
  </div>
3226
3410
  <div class="flex justify-end gap-2">
3227
3411
  <p-button
3228
3412
  id="oip-menu-item-create-cancel"
3229
3413
  label="{{ 'menuItemCreateDialogComponent.cancel' | translate }}"
3230
3414
  severity="secondary"
3231
- (click)="changeVisible()"
3232
- (keydown)="changeVisible()" />
3415
+ [disabled]="saving"
3416
+ (click)="changeVisible()" />
3233
3417
  <p-button
3234
3418
  id="oip-menu-item-create-save"
3235
3419
  label="{{ 'menuItemCreateDialogComponent.save' | translate }}"
3236
- (click)="save()"
3237
- (keydown)="save()" />
3420
+ [disabled]="saving"
3421
+ [loading]="saving"
3422
+ (click)="save()" />
3238
3423
  </div>
3239
3424
  </p-dialog>
3240
3425
  `
@@ -3249,9 +3434,17 @@ class MenuItemEditDialogComponent {
3249
3434
  constructor() {
3250
3435
  this.menuService = inject(MenuService);
3251
3436
  this.securityDataService = inject(SecurityDataService);
3437
+ this.msgService = inject(MsgService);
3252
3438
  this.visibleChange = new EventEmitter();
3253
3439
  this.modules = [];
3254
3440
  this.roles = [];
3441
+ this.iconOptions = Object.values(PrimeIcons)
3442
+ .filter((icon) => typeof icon === 'string')
3443
+ .map((icon) => ({
3444
+ label: icon.replace('pi pi-', ''),
3445
+ value: icon
3446
+ }))
3447
+ .sort((left, right) => left.label.localeCompare(right.label));
3255
3448
  this.item = {
3256
3449
  icon: '',
3257
3450
  label: '',
@@ -3260,15 +3453,28 @@ class MenuItemEditDialogComponent {
3260
3453
  moduleInstanceId: 0,
3261
3454
  parentId: 0
3262
3455
  };
3456
+ this.saving = false;
3263
3457
  }
3264
3458
  changeVisible() {
3265
3459
  this.visible = !this.visible;
3266
3460
  this.visibleChange.emit(this.visible);
3267
3461
  }
3268
3462
  async save() {
3269
- await this.menuService.editModuleInstance(this.item);
3270
- await this.menuService.loadMenu();
3271
- this.hide();
3463
+ if (this.saving) {
3464
+ return;
3465
+ }
3466
+ this.saving = true;
3467
+ try {
3468
+ await this.menuService.editModuleInstance(this.item);
3469
+ await this.menuService.loadMenu();
3470
+ this.hide();
3471
+ }
3472
+ catch (error) {
3473
+ this.msgService.error(error);
3474
+ }
3475
+ finally {
3476
+ this.saving = false;
3477
+ }
3272
3478
  }
3273
3479
  hide() {
3274
3480
  this.visible = false;
@@ -3296,7 +3502,8 @@ class MenuItemEditDialogComponent {
3296
3502
  header="{{ 'menuItemEditDialogComponent.header' | translate }}"
3297
3503
  [modal]="true"
3298
3504
  [style]="{ width: '40rem' }"
3299
- [(visible)]="visible">
3505
+ [(visible)]="visible"
3506
+ (keydown.enter)="save()">
3300
3507
  <div class="flex items-center gap-4 mb-4 mt-1">
3301
3508
  <label class="font-semibold w-1/3" for="oip-menu-item-edit-dialog-menu-input">
3302
3509
  {{ 'menuItemEditDialogComponent.label' | translate }}
@@ -3313,8 +3520,30 @@ class MenuItemEditDialogComponent {
3313
3520
  <label class="font-semibold w-1/3" for="oip-menu-item-edit-dialog-icon">
3314
3521
  {{ 'menuItemEditDialogComponent.icon' | translate }}
3315
3522
  </label>
3316
- <i class="{{ item.icon }}"></i>
3317
- <input class="flex-auto" id="oip-menu-item-edit-dialog-icon" pInputText [(ngModel)]="item.icon" />
3523
+ <p-select
3524
+ appendTo="body"
3525
+ class="flex-auto"
3526
+ filterBy="label,value"
3527
+ id="oip-menu-item-edit-dialog-icon"
3528
+ optionLabel="label"
3529
+ optionValue="value"
3530
+ scrollHeight="18rem"
3531
+ [filter]="true"
3532
+ [options]="iconOptions"
3533
+ [(ngModel)]="item.icon">
3534
+ <ng-template let-icon pTemplate="selectedItem">
3535
+ <div class="flex items-center gap-2">
3536
+ <i [class]="icon.value"></i>
3537
+ <span>{{ icon.label }}</span>
3538
+ </div>
3539
+ </ng-template>
3540
+ <ng-template let-icon pTemplate="item">
3541
+ <div class="flex items-center gap-2">
3542
+ <i [class]="icon.value"></i>
3543
+ <span>{{ icon.label }}</span>
3544
+ </div>
3545
+ </ng-template>
3546
+ </p-select>
3318
3547
  </div>
3319
3548
 
3320
3549
  <div class="flex items-center gap-4 mb-4">
@@ -3336,21 +3565,22 @@ class MenuItemEditDialogComponent {
3336
3565
  id="oip-menu-item-edit-dialog-cancel-edit-button"
3337
3566
  label="{{ 'menuItemEditDialogComponent.cancel' | translate }}"
3338
3567
  severity="secondary"
3339
- (click)="changeVisible()"
3340
- (keydown)="changeVisible()" />
3568
+ [disabled]="saving"
3569
+ (click)="changeVisible()" />
3341
3570
  <p-button
3342
3571
  id="oip-menu-item-edit-dialog-save-edit-button"
3343
3572
  label="{{ 'menuItemEditDialogComponent.save' | translate }}"
3344
- (click)="save()"
3345
- (keydown)="save()" />
3573
+ [disabled]="saving"
3574
+ [loading]="saving"
3575
+ (click)="save()" />
3346
3576
  </div>
3347
3577
  </p-dialog>
3348
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i2$3.Dialog, selector: "p-dialog", inputs: ["hostName", "header", "draggable", "resizable", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "appendTo", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3$3.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i1.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
3578
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i2$3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i3$3.Dialog, selector: "p-dialog", inputs: ["hostName", "header", "draggable", "resizable", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "appendTo", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i5.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i1.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "styleClass", "panelStyle", "panelStyleClass", "inputId", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "dataKey", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "showClear", "autofocus", "placeholder", "options", "filterValue", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect", "size", "variant", "fluid", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
3349
3579
  }
3350
3580
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MenuItemEditDialogComponent, decorators: [{
3351
3581
  type: Component,
3352
3582
  args: [{
3353
- imports: [ButtonModule, DialogModule, InputTextModule, FormsModule, TranslatePipe, MultiSelectModule],
3583
+ imports: [ButtonModule, DialogModule, InputTextModule, SelectModule, FormsModule, TranslatePipe, MultiSelectModule],
3354
3584
  selector: 'menu-item-edit-dialog',
3355
3585
  standalone: true,
3356
3586
  template: `
@@ -3358,7 +3588,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
3358
3588
  header="{{ 'menuItemEditDialogComponent.header' | translate }}"
3359
3589
  [modal]="true"
3360
3590
  [style]="{ width: '40rem' }"
3361
- [(visible)]="visible">
3591
+ [(visible)]="visible"
3592
+ (keydown.enter)="save()">
3362
3593
  <div class="flex items-center gap-4 mb-4 mt-1">
3363
3594
  <label class="font-semibold w-1/3" for="oip-menu-item-edit-dialog-menu-input">
3364
3595
  {{ 'menuItemEditDialogComponent.label' | translate }}
@@ -3375,8 +3606,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
3375
3606
  <label class="font-semibold w-1/3" for="oip-menu-item-edit-dialog-icon">
3376
3607
  {{ 'menuItemEditDialogComponent.icon' | translate }}
3377
3608
  </label>
3378
- <i class="{{ item.icon }}"></i>
3379
- <input class="flex-auto" id="oip-menu-item-edit-dialog-icon" pInputText [(ngModel)]="item.icon" />
3609
+ <p-select
3610
+ appendTo="body"
3611
+ class="flex-auto"
3612
+ filterBy="label,value"
3613
+ id="oip-menu-item-edit-dialog-icon"
3614
+ optionLabel="label"
3615
+ optionValue="value"
3616
+ scrollHeight="18rem"
3617
+ [filter]="true"
3618
+ [options]="iconOptions"
3619
+ [(ngModel)]="item.icon">
3620
+ <ng-template let-icon pTemplate="selectedItem">
3621
+ <div class="flex items-center gap-2">
3622
+ <i [class]="icon.value"></i>
3623
+ <span>{{ icon.label }}</span>
3624
+ </div>
3625
+ </ng-template>
3626
+ <ng-template let-icon pTemplate="item">
3627
+ <div class="flex items-center gap-2">
3628
+ <i [class]="icon.value"></i>
3629
+ <span>{{ icon.label }}</span>
3630
+ </div>
3631
+ </ng-template>
3632
+ </p-select>
3380
3633
  </div>
3381
3634
 
3382
3635
  <div class="flex items-center gap-4 mb-4">
@@ -3398,13 +3651,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
3398
3651
  id="oip-menu-item-edit-dialog-cancel-edit-button"
3399
3652
  label="{{ 'menuItemEditDialogComponent.cancel' | translate }}"
3400
3653
  severity="secondary"
3401
- (click)="changeVisible()"
3402
- (keydown)="changeVisible()" />
3654
+ [disabled]="saving"
3655
+ (click)="changeVisible()" />
3403
3656
  <p-button
3404
3657
  id="oip-menu-item-edit-dialog-save-edit-button"
3405
3658
  label="{{ 'menuItemEditDialogComponent.save' | translate }}"
3406
- (click)="save()"
3407
- (keydown)="save()" />
3659
+ [disabled]="saving"
3660
+ [loading]="saving"
3661
+ (click)="save()" />
3408
3662
  </div>
3409
3663
  </p-dialog>
3410
3664
  `
@@ -4355,7 +4609,7 @@ class DbMigrationComponent extends BaseModuleComponent {
4355
4609
  } @else if (isSecurity) {
4356
4610
  <security [controller]="controller" [id]="id"/>
4357
4611
  }
4358
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: TableModule }, { kind: "component", type: i1$6.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i2$6.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i1$6.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "directive", type: i1$6.EditableRow, selector: "[pEditableRow]", inputs: ["pEditableRow", "pEditableRowDisabled"] }, { kind: "directive", type: i1$6.CancelEditableRow, selector: "[pCancelEditableRow]" }, { kind: "component", type: i1$6.ColumnFilter, selector: "p-columnFilter, p-column-filter, p-columnfilter", inputs: ["field", "type", "display", "showMenu", "matchMode", "operator", "showOperator", "showClearButton", "showApplyButton", "showMatchModes", "showAddButton", "hideOnClear", "placeholder", "matchModeOptions", "maxConstraints", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "locale", "localeMatcher", "currency", "currencyDisplay", "filterOn", "useGrouping", "showButtons", "ariaLabel", "filterButtonProps"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: SharedModule }, { kind: "ngmodule", type: TagModule }, { kind: "ngmodule", type: InputTextModule }, { kind: "ngmodule", type: TextareaModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: ConfirmDialog, selector: "p-confirmDialog, p-confirmdialog, p-confirm-dialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "modal", "visible", "position", "draggable"], outputs: ["onHide"] }, { kind: "component", type: SecurityComponent, selector: "security", inputs: ["id", "controller"] }, { kind: "directive", type: Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
4612
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: TableModule }, { kind: "component", type: i1$6.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i2$3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i1$6.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "directive", type: i1$6.EditableRow, selector: "[pEditableRow]", inputs: ["pEditableRow", "pEditableRowDisabled"] }, { kind: "directive", type: i1$6.CancelEditableRow, selector: "[pCancelEditableRow]" }, { kind: "component", type: i1$6.ColumnFilter, selector: "p-columnFilter, p-column-filter, p-columnfilter", inputs: ["field", "type", "display", "showMenu", "matchMode", "operator", "showOperator", "showClearButton", "showApplyButton", "showMatchModes", "showAddButton", "hideOnClear", "placeholder", "matchModeOptions", "maxConstraints", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "locale", "localeMatcher", "currency", "currencyDisplay", "filterOn", "useGrouping", "showButtons", "ariaLabel", "filterButtonProps"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: SharedModule }, { kind: "ngmodule", type: TagModule }, { kind: "ngmodule", type: InputTextModule }, { kind: "ngmodule", type: TextareaModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: ConfirmDialog, selector: "p-confirmDialog, p-confirmdialog, p-confirm-dialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "modal", "visible", "position", "draggable"], outputs: ["onHide"] }, { kind: "component", type: SecurityComponent, selector: "security", inputs: ["id", "controller"] }, { kind: "directive", type: Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
4359
4613
  }
4360
4614
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DbMigrationComponent, decorators: [{
4361
4615
  type: Component,
@@ -4463,18 +4717,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
4463
4717
  class ModuleApi extends HttpClient {
4464
4718
  constructor() {
4465
4719
  super(...arguments);
4466
- /**
4467
- * @description Retrieves all modules stored in the system.
4468
- *
4469
- * @tags Module
4470
- * @name getAll
4471
- * @summary Retrieves all modules stored in the system.
4472
- * @request GET:/api/module/get-all
4473
- * @secure
4474
- * @response `200` `(ModuleDto)[]` OK
4475
- * @response `401` `ApiExceptionResponse` Unauthorized
4476
- * @response `403` `ApiExceptionResponse` Forbidden
4477
- */
4478
4720
  this.getAll = (params = {}) => this.request({
4479
4721
  path: `/api/module/get-all`,
4480
4722
  method: "GET",
@@ -4482,18 +4724,6 @@ class ModuleApi extends HttpClient {
4482
4724
  format: "json",
4483
4725
  ...params,
4484
4726
  });
4485
- /**
4486
- * @description Inserts a new module into the system.
4487
- *
4488
- * @tags Module
4489
- * @name insert
4490
- * @summary Inserts a new module into the system.
4491
- * @request POST:/api/module/insert
4492
- * @secure
4493
- * @response `200` `void` OK
4494
- * @response `401` `ApiExceptionResponse` Unauthorized
4495
- * @response `403` `ApiExceptionResponse` Forbidden
4496
- */
4497
4727
  this.insert = (data, params = {}) => this.request({
4498
4728
  path: `/api/module/insert`,
4499
4729
  method: "POST",
@@ -4502,18 +4732,6 @@ class ModuleApi extends HttpClient {
4502
4732
  type: ContentType.Json,
4503
4733
  ...params,
4504
4734
  });
4505
- /**
4506
- * @description Deletes a module by its identifier.
4507
- *
4508
- * @tags Module
4509
- * @name delete
4510
- * @summary Deletes a module by its identifier.
4511
- * @request DELETE:/api/module/delete
4512
- * @secure
4513
- * @response `200` `void` OK
4514
- * @response `401` `ApiExceptionResponse` Unauthorized
4515
- * @response `403` `ApiExceptionResponse` Forbidden
4516
- */
4517
4735
  this.delete = (data, params = {}) => this.request({
4518
4736
  path: `/api/module/delete`,
4519
4737
  method: "DELETE",
@@ -4522,18 +4740,6 @@ class ModuleApi extends HttpClient {
4522
4740
  type: ContentType.Json,
4523
4741
  ...params,
4524
4742
  });
4525
- /**
4526
- * @description Returns all registered modules and indicates whether each one is currently loaded into the application.
4527
- *
4528
- * @tags Module
4529
- * @name getModulesWithLoadStatus
4530
- * @summary Returns all registered modules and indicates whether each one is currently loaded into the application.
4531
- * @request GET:/api/module/get-modules-with-load-status
4532
- * @secure
4533
- * @response `200` `(ExistModuleDto)[]` OK
4534
- * @response `401` `ApiExceptionResponse` Unauthorized
4535
- * @response `403` `ApiExceptionResponse` Forbidden
4536
- */
4537
4743
  this.getModulesWithLoadStatus = (params = {}) => this.request({
4538
4744
  path: `/api/module/get-modules-with-load-status`,
4539
4745
  method: "GET",
@@ -4556,52 +4762,57 @@ class AppModulesComponent {
4556
4762
  this.msgService = inject(MsgService);
4557
4763
  this.confirmationService = inject(ConfirmationService);
4558
4764
  this.l10nService = inject(L10nService);
4559
- this.l10n = {};
4560
4765
  this.titleService = inject(AppTitleService);
4561
4766
  this.moduleService = inject(ModuleApi);
4767
+ this.translationsReady = firstValueFrom(this.l10nService.loadComponentTranslations('app-modules'));
4562
4768
  }
4563
4769
  async ngOnInit() {
4564
- this.l10nService.get('app-modules').subscribe((l10n) => {
4565
- this.l10n = l10n;
4566
- });
4567
- this.titleService.setTitle(this.l10n.title);
4770
+ await this.translationsReady;
4771
+ this.titleService.setTitle(this.t('app-modules.title'));
4568
4772
  await this.refreshAction();
4569
4773
  }
4570
4774
  async refreshAction() {
4571
4775
  this.modules = await this.moduleService.getModulesWithLoadStatus();
4572
4776
  }
4573
- deleteModule(module) {
4777
+ async deleteModule(module) {
4778
+ await this.translationsReady;
4574
4779
  this.confirmationService.confirm({
4575
- header: this.l10n.confirm.header,
4576
- message: this.l10n.confirm.message,
4780
+ header: this.t('app-modules.confirm.header'),
4781
+ message: this.t('app-modules.confirm.message'),
4577
4782
  icon: 'pi pi-trash',
4578
4783
  rejectButtonProps: {
4579
- label: this.l10n.confirm.cancel,
4784
+ label: this.t('app-modules.confirm.cancel'),
4580
4785
  severity: 'secondary',
4581
4786
  outlined: true
4582
4787
  },
4583
4788
  acceptButtonProps: {
4584
- label: this.l10n.confirm.delete,
4789
+ label: this.t('app-modules.confirm.delete'),
4585
4790
  severity: 'danger'
4586
4791
  },
4587
4792
  accept: async () => {
4588
- this.dataService
4589
- .sendRequest(`api/module/delete`, 'DELETE', {
4590
- moduleId: module.moduleId
4591
- })
4592
- .then(() => this.refreshAction())
4593
- .catch((error) => console.error(error));
4594
- this.msgService.success(this.l10n.messages.deleteSuccess);
4793
+ try {
4794
+ await this.dataService.sendRequest(`api/module/delete`, 'DELETE', {
4795
+ moduleId: module.moduleId
4796
+ });
4797
+ await this.refreshAction();
4798
+ this.msgService.success(this.t('app-modules.messages.deleteSuccess'));
4799
+ }
4800
+ catch (error) {
4801
+ this.msgService.error(error);
4802
+ }
4595
4803
  }
4596
4804
  });
4597
4805
  }
4806
+ t(key) {
4807
+ return this.l10nService.instant(key);
4808
+ }
4598
4809
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AppModulesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4599
4810
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: AppModulesComponent, isStandalone: true, selector: "app-modules", providers: [ConfirmationService, ModuleApi], ngImport: i0, template: `
4600
4811
  <p-confirmDialog></p-confirmDialog>
4601
4812
  <div class="flex flex-col md:flex-row gap-4">
4602
4813
  <div class="card w-full">
4603
4814
  <div class="font-semibold text-xl mb-4">
4604
- {{ l10n.title }}
4815
+ {{ 'app-modules.title' | translate }}
4605
4816
  </div>
4606
4817
  <div class="mb-4">
4607
4818
  <p-toolbar>
@@ -4650,7 +4861,7 @@ class AppModulesComponent {
4650
4861
  </p-table>
4651
4862
  </div>
4652
4863
  </div>
4653
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i1$6.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i2$6.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "component", type: Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ToolbarModule }, { kind: "component", type: i4$1.Toolbar, selector: "p-toolbar", inputs: ["styleClass", "ariaLabelledBy"] }, { kind: "directive", type: Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip"] }, { kind: "component", type: ConfirmDialog, selector: "p-confirmDialog, p-confirmdialog, p-confirm-dialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "modal", "visible", "position", "draggable"], outputs: ["onHide"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
4864
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i1$6.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i2$3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "component", type: Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ToolbarModule }, { kind: "component", type: i4$1.Toolbar, selector: "p-toolbar", inputs: ["styleClass", "ariaLabelledBy"] }, { kind: "directive", type: Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip"] }, { kind: "component", type: ConfirmDialog, selector: "p-confirmDialog, p-confirmdialog, p-confirm-dialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "modal", "visible", "position", "draggable"], outputs: ["onHide"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
4654
4865
  }
4655
4866
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AppModulesComponent, decorators: [{
4656
4867
  type: Component,
@@ -4663,7 +4874,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
4663
4874
  <div class="flex flex-col md:flex-row gap-4">
4664
4875
  <div class="card w-full">
4665
4876
  <div class="font-semibold text-xl mb-4">
4666
- {{ l10n.title }}
4877
+ {{ 'app-modules.title' | translate }}
4667
4878
  </div>
4668
4879
  <div class="mb-4">
4669
4880
  <p-toolbar>
@@ -4714,7 +4925,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
4714
4925
  </div>
4715
4926
  `
4716
4927
  }]
4717
- }] });
4928
+ }], ctorParameters: () => [] });
4718
4929
 
4719
4930
  /* eslint-disable */
4720
4931
  /* tslint:disable */
@@ -5719,7 +5930,7 @@ class DiscussionComponent {
5719
5930
  </div>
5720
5931
  }
5721
5932
  </section>
5722
- `, isInline: true, styles: [":host{display:block}.file-trigger{position:relative;display:inline-flex}.file-trigger input{position:absolute;inset:0;opacity:0;cursor:pointer}.markdown-preview,.history-markdown{word-break:break-word}.markdown-preview :is(h1,h2,h3),.history-markdown :is(h1,h2,h3){margin:0 0 .75rem;font-weight:600}.markdown-preview a,.history-markdown a{color:#2563eb;text-decoration:underline;text-underline-offset:.18em}.emoji-popover{display:flex;flex-wrap:wrap;gap:.5rem;max-width:16rem}.emoji-option{display:inline-flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;font-size:1.25rem;cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i2$2.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "directive", type: i2$6.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i5.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "component", type: ConfirmDialog, selector: "p-confirmDialog, p-confirmdialog, p-confirm-dialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "modal", "visible", "position", "draggable"], outputs: ["onHide"] }, { kind: "ngmodule", type: DividerModule }, { kind: "ngmodule", type: PanelModule }, { kind: "component", type: i6.Panel, selector: "p-panel", inputs: ["id", "toggleable", "header", "collapsed", "styleClass", "iconPos", "showHeader", "toggler", "transitionOptions", "toggleButtonProps"], outputs: ["collapsedChange", "onBeforeToggle", "onAfterToggle"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i7.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: ProgressSpinnerModule }, { kind: "component", type: i8.ProgressSpinner, selector: "p-progressSpinner, p-progress-spinner, p-progressspinner", inputs: ["styleClass", "strokeWidth", "fill", "animationDuration", "ariaLabel"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i9.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["autoResize", "pSize", "variant", "fluid", "invalid"], outputs: ["onResize"] }, { kind: "pipe", type: i2$1.DatePipe, name: "date" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
5933
+ `, isInline: true, styles: [":host{display:block}.file-trigger{position:relative;display:inline-flex}.file-trigger input{position:absolute;inset:0;opacity:0;cursor:pointer}.markdown-preview,.history-markdown{word-break:break-word}.markdown-preview :is(h1,h2,h3),.history-markdown :is(h1,h2,h3){margin:0 0 .75rem;font-weight:600}.markdown-preview a,.history-markdown a{color:#2563eb;text-decoration:underline;text-underline-offset:.18em}.emoji-popover{display:flex;flex-wrap:wrap;gap:.5rem;max-width:16rem}.emoji-option{display:inline-flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;font-size:1.25rem;cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i2$2.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "directive", type: i2$3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i5$1.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "component", type: ConfirmDialog, selector: "p-confirmDialog, p-confirmdialog, p-confirm-dialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "modal", "visible", "position", "draggable"], outputs: ["onHide"] }, { kind: "ngmodule", type: DividerModule }, { kind: "ngmodule", type: PanelModule }, { kind: "component", type: i6.Panel, selector: "p-panel", inputs: ["id", "toggleable", "header", "collapsed", "styleClass", "iconPos", "showHeader", "toggler", "transitionOptions", "toggleButtonProps"], outputs: ["collapsedChange", "onBeforeToggle", "onAfterToggle"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i7.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: ProgressSpinnerModule }, { kind: "component", type: i8.ProgressSpinner, selector: "p-progressSpinner, p-progress-spinner, p-progressspinner", inputs: ["styleClass", "strokeWidth", "fill", "animationDuration", "ariaLabel"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i9.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["autoResize", "pSize", "variant", "fluid", "invalid"], outputs: ["onResize"] }, { kind: "pipe", type: i2$1.DatePipe, name: "date" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
5723
5934
  }
5724
5935
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DiscussionComponent, decorators: [{
5725
5936
  type: Component,