oip-common 0.4.0 → 0.6.0

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
- import { Injectable, inject, signal, computed, effect, DestroyRef, ChangeDetectorRef, Component, Input, InjectionToken, PLATFORM_ID, ViewChild, HostBinding, EventEmitter, Output, Renderer2, SecurityContext, ChangeDetectionStrategy, makeEnvironmentProviders, importProvidersFrom } from '@angular/core';
2
+ import { Injectable, inject, signal, computed, effect, DestroyRef, ChangeDetectorRef, Component, Input, InjectionToken, PLATFORM_ID, ViewChild, HostBinding, EventEmitter, Output, Renderer2, SecurityContext, ChangeDetectionStrategy, Injector, EnvironmentInjector, ViewContainerRef, makeEnvironmentProviders, importProvidersFrom } from '@angular/core';
3
3
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
- import * as i2$2 from 'primeng/api';
4
+ import { Subject, BehaviorSubject, of, tap, shareReplay, ReplaySubject, firstValueFrom, from, filter as filter$1 } from 'rxjs';
5
+ import * as i2$3 from 'primeng/api';
5
6
  import { MessageService, ConfirmationService, PrimeIcons, SharedModule } from 'primeng/api';
6
7
  import { HttpErrorResponse, HttpClient as HttpClient$1, provideHttpClient, withInterceptors, withFetch } from '@angular/common/http';
7
- import * as i2$3 from '@ngx-translate/core';
8
+ import * as i2$4 from '@ngx-translate/core';
8
9
  import { TranslateService, TranslatePipe, TranslateModule, TranslateLoader } from '@ngx-translate/core';
9
10
  import * as i1$5 from '@angular/router';
10
- import { ActivatedRoute, Router, RouterModule, NavigationEnd, RouterLinkActive, RouterLink } from '@angular/router';
11
- import { BehaviorSubject, Subject, of, tap, shareReplay, ReplaySubject, firstValueFrom, from, filter as filter$1 } from 'rxjs';
11
+ import { Router, ActivatedRoute, RouterModule, NavigationEnd, RouterLinkActive, RouterLink } from '@angular/router';
12
12
  import { tap as tap$1, catchError, filter, distinctUntilChanged, switchMap, map, take } from 'rxjs/operators';
13
13
  import { Title, DomSanitizer } from '@angular/platform-browser';
14
14
  import { PrimeNG } from 'primeng/config';
@@ -24,7 +24,7 @@ import * as i1$3 from '@angular/common';
24
24
  import { isPlatformBrowser, CommonModule, NgComponentOutlet, NgClass, DatePipe, PathLocationStrategy, LocationStrategy } from '@angular/common';
25
25
  import * as i3$3 from 'primeng/styleclass';
26
26
  import { StyleClassModule } from 'primeng/styleclass';
27
- import { updatePreset, updateSurfacePalette, $t } from '@primeng/themes';
27
+ import { updatePreset, updateSurfacePalette, $t, definePreset } from '@primeng/themes';
28
28
  import Aura from '@primeng/themes/aura';
29
29
  import Lara from '@primeng/themes/lara';
30
30
  import Nora from '@primeng/themes/nora';
@@ -49,11 +49,11 @@ import * as i3$2 from 'primeng/menu';
49
49
  import { MenuModule } from 'primeng/menu';
50
50
  import * as i1$6 from 'primeng/contextmenu';
51
51
  import { ContextMenuModule, ContextMenu } from 'primeng/contextmenu';
52
- import * as i3$5 from 'primeng/dialog';
52
+ import * as i3$4 from 'primeng/dialog';
53
53
  import { DialogModule } from 'primeng/dialog';
54
54
  import * as i4$1 from 'primeng/inputtext';
55
55
  import { InputTextModule, InputText } from 'primeng/inputtext';
56
- import * as i3$4 from 'primeng/ripple';
56
+ import * as i2$2 from 'primeng/ripple';
57
57
  import { RippleModule } from 'primeng/ripple';
58
58
  import * as i5 from 'primeng/select';
59
59
  import { SelectModule, Select } from 'primeng/select';
@@ -62,16 +62,18 @@ import { FileUploadModule } from 'primeng/fileupload';
62
62
  import { ImageModule } from 'primeng/image';
63
63
  import * as i1$8 from 'primeng/table';
64
64
  import { TableModule } from 'primeng/table';
65
- import * as i2$4 from 'primeng/toggleswitch';
65
+ import * as i2$5 from 'primeng/toggleswitch';
66
66
  import { ToggleSwitchModule } from 'primeng/toggleswitch';
67
67
  import { TextareaModule, Textarea } from 'primeng/textarea';
68
- import * as i4$2 from 'primeng/toolbar';
68
+ import * as i5$1 from 'primeng/toolbar';
69
69
  import { ToolbarModule } from 'primeng/toolbar';
70
- import * as i5$1 from 'primeng/card';
70
+ import * as i5$2 from 'primeng/card';
71
71
  import { CardModule } from 'primeng/card';
72
72
  import { DividerModule } from 'primeng/divider';
73
73
  import * as i6 from 'primeng/panel';
74
74
  import { PanelModule } from 'primeng/panel';
75
+ import { loadRemoteModule } from '@angular-architects/module-federation';
76
+ import { primitive } from '@primeng/themes/aura/base';
75
77
  import { TranslateHttpLoader } from '@ngx-translate/http-loader';
76
78
 
77
79
  class TopBarService {
@@ -89,22 +91,24 @@ class TopBarService {
89
91
  }
90
92
  set activeId(value) {
91
93
  this._activeId = value;
92
- if (this.activeTopBarItem?.click)
93
- this.activeTopBarItem.click();
94
+ const activeTopBarItem = this.activeTopBarItem;
95
+ if (activeTopBarItem?.click)
96
+ activeTopBarItem.click();
97
+ this.activeIdSubject.next(value);
94
98
  }
95
99
  constructor() {
96
100
  this.topBarItems = [];
101
+ this.activeIdSubject = new Subject();
102
+ this.activeId$ = this.activeIdSubject.asObservable();
97
103
  }
98
104
  // Set tob bar items
99
105
  setTopBarItems(items) {
100
106
  this.topBarItems = items;
101
107
  }
102
108
  checkId(id) {
103
- if (this.activeTopBarItem === undefined && id === 'content')
104
- return true;
105
- if (this.activeTopBarItem === undefined)
106
- return false;
107
- return this.activeTopBarItem.id == id;
109
+ const activeTopBarItem = this.activeTopBarItem;
110
+ const result = activeTopBarItem === undefined ? id === 'content' : activeTopBarItem.id == id;
111
+ return result;
108
112
  }
109
113
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: TopBarService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
110
114
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: TopBarService, providedIn: 'root' }); }
@@ -150,18 +154,17 @@ class MsgService {
150
154
  });
151
155
  }
152
156
  error(detail, summary = this.translate.instant('msgService.error'), life = this.lifetime) {
153
- const validationMessage = this.extractValidationMessage(detail);
154
- if (validationMessage) {
155
- detail = validationMessage;
156
- }
157
- else if (detail instanceof HttpErrorResponse) {
157
+ if (detail instanceof HttpErrorResponse) {
158
158
  summary = `Error: ${detail.status} ${detail.statusText}`;
159
- detail = `${detail.name} \r\n ${detail.message}`;
159
+ detail = this.extractErrorMessage(detail, `${detail.name} \r\n ${detail.message}`);
160
+ }
161
+ else {
162
+ detail = this.extractErrorMessage(detail, detail?.toString?.() ?? this.translate.instant('msgService.error'));
160
163
  }
161
164
  this.messageService.add({
162
165
  severity: 'error',
163
166
  summary: summary,
164
- detail: detail.toString(),
167
+ detail: detail,
165
168
  life: life
166
169
  });
167
170
  }
@@ -170,14 +173,9 @@ class MsgService {
170
173
  if (validationMessage) {
171
174
  return validationMessage;
172
175
  }
173
- if (typeof error === 'object' &&
174
- error &&
175
- 'error' in error &&
176
- typeof error.error === 'object' &&
177
- error.error &&
178
- 'message' in error.error &&
179
- typeof error.error.message === 'string') {
180
- return error.error.message;
176
+ const apiExceptionMessage = this.extractApiExceptionMessage(error);
177
+ if (apiExceptionMessage) {
178
+ return apiExceptionMessage;
181
179
  }
182
180
  if (typeof error === 'object' && error && 'message' in error && typeof error.message === 'string') {
183
181
  return error.message;
@@ -195,6 +193,15 @@ class MsgService {
195
193
  .filter((message) => message.length > 0);
196
194
  return messages.length > 0 ? messages.join('\n') : null;
197
195
  }
196
+ extractApiExceptionMessage(error) {
197
+ const responseError = this.getObjectProperty(error, 'error');
198
+ const message = this.getStringProperty(responseError, 'message') ?? this.getStringProperty(error, 'message');
199
+ const title = this.getStringProperty(responseError, 'title') ?? this.getStringProperty(error, 'title');
200
+ if (message && title) {
201
+ return `${title}: ${message}`;
202
+ }
203
+ return message ?? title;
204
+ }
198
205
  toValidationMessages(field, value) {
199
206
  if (Array.isArray(value)) {
200
207
  return value
@@ -213,6 +220,13 @@ class MsgService {
213
220
  const value = source[property];
214
221
  return typeof value === 'object' && value !== null ? value : null;
215
222
  }
223
+ getStringProperty(source, property) {
224
+ if (typeof source !== 'object' || source === null || !(property in source)) {
225
+ return null;
226
+ }
227
+ const value = source[property];
228
+ return typeof value === 'string' && value.length > 0 ? value : null;
229
+ }
216
230
  errorFromException(error, fallback, summary = fallback, life = this.lifetime) {
217
231
  this.error(this.extractErrorMessage(error, fallback), summary, life);
218
232
  }
@@ -399,6 +413,7 @@ class LayoutService {
399
413
  return ngDateFormat;
400
414
  }, ...(ngDevMode ? [{ debugName: "monthFormat" }] : []));
401
415
  this.timeZone = computed(() => this.layoutConfig().timeZone, ...(ngDevMode ? [{ debugName: "timeZone" }] : []));
416
+ this.adminMode = computed(() => this.layoutConfig().adminMode ?? false, ...(ngDevMode ? [{ debugName: "adminMode" }] : []));
402
417
  this.transitionComplete = signal(false, ...(ngDevMode ? [{ debugName: "transitionComplete" }] : []));
403
418
  this.initialized = false;
404
419
  effect(() => {
@@ -437,7 +452,8 @@ class LayoutService {
437
452
  dateFormat: 'yyyy-MM-dd',
438
453
  timeFormat: 'HH:mm:ss',
439
454
  dateTimeFormat: 'yyyy-MM-dd HH:mm:ss',
440
- timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone
455
+ timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
456
+ adminMode: false
441
457
  };
442
458
  }
443
459
  handleDarkModeTransition(config) {
@@ -522,10 +538,539 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
522
538
  args: [{ providedIn: 'root' }]
523
539
  }], ctorParameters: () => [] });
524
540
 
541
+ var msgService$1 = {
542
+ success: "Success",
543
+ info: "Info",
544
+ warn: "Warn",
545
+ error: "Error",
546
+ secondary: "Secondary"
547
+ };
548
+ var securityComponent$1 = {
549
+ security: "Security",
550
+ save: "Save",
551
+ savedSecurity: "Security settings saved",
552
+ selectRoles: "Select roles"
553
+ };
554
+ var profileComponent$1 = {
555
+ changePhoto: "Upload Photo",
556
+ successfullyUploaded: "Uploaded successfully",
557
+ failedToLoadPhoto: "Failed to load user photo"
558
+ };
559
+ var configComponent$1 = {
560
+ profile: "Profile",
561
+ photo: "Photo",
562
+ usePhoto256x256Pixel: "Use a 256x256 pixel photo",
563
+ localization: "Language",
564
+ selectLanguage: "Select language"
565
+ };
566
+ var baseComponent$1 = {
567
+ content: "Content",
568
+ settings: "Settings",
569
+ security: "Security",
570
+ success: "Saved successfully"
571
+ };
572
+ var menuComponent$1 = {
573
+ all: "All",
574
+ "new": "Add"
575
+ };
576
+ var menuItemEditDialogComponent$1 = {
577
+ header: "Edit menu item",
578
+ parentLabel: "Parent item",
579
+ label: "Label",
580
+ module: "Module",
581
+ selectModule: "Select module",
582
+ icon: "Icon",
583
+ cancel: "Cancel",
584
+ save: "Save",
585
+ security: "Read access"
586
+ };
587
+ var menuItemCreateDialogComponent$1 = {
588
+ header: "New menu item",
589
+ parentLabel: "Parent item",
590
+ label: "Label",
591
+ module: "Module",
592
+ selectModule: "Select module",
593
+ searchModule: "Search module",
594
+ icon: "Icon",
595
+ cancel: "Cancel",
596
+ save: "Save"
597
+ };
598
+ var unauthorized$1 = {
599
+ welcomeToOip: "Welcome to OIP!",
600
+ signInToContinue: "Sign in to continue",
601
+ signIn: "Sign In"
602
+ };
603
+ var menuItemComponent$1 = {
604
+ "new": "Add",
605
+ edit: "Edit",
606
+ "delete": "Delete",
607
+ deleteItemConfirmHeader: "Delete",
608
+ deleteItemConfirmMessage: "Are you sure you want to delete the item?",
609
+ deleteItemSuccessMessage: "Deleted successfully",
610
+ deleteItemConfirmRejectButtonPropsLabel: "Cancel",
611
+ deleteItemConfirmAcceptButtonPropsLabel: "Delete",
612
+ moveUp: "Move up",
613
+ moveDown: "Move down"
614
+ };
615
+ var topbar$1 = {
616
+ logout: "Logout",
617
+ logoutConfirmHeader: "Logout",
618
+ logoutConfirmMessage: "Are you sure you want to logout?",
619
+ logoutConfirmCancel: "Cancel",
620
+ logoutConfirmAccept: "Logout",
621
+ profile: "Profile",
622
+ applications: "Applications"
623
+ };
624
+ var userNotifications$1 = {
625
+ title: "Notifications",
626
+ empty: "No unread notifications",
627
+ refresh: "Refresh",
628
+ markAsRead: "Read",
629
+ markedAsRead: "Notification marked as read"
630
+ };
631
+ var discussionComponent$1 = {
632
+ writeCommentPlaceholder: "Write a comment. Use @email or @first.last for mentions.",
633
+ mentionSuggestions: "Mention suggestions",
634
+ edit: "Edit",
635
+ preview: "Preview",
636
+ attachFiles: "Attach files",
637
+ send: "Send",
638
+ loadingComments: "Loading comments...",
639
+ noCommentsYet: "No comments yet.",
640
+ edited: "Edited",
641
+ editCommentPlaceholder: "Edit comment",
642
+ addAttachment: "Add attachment",
643
+ save: "Save",
644
+ cancel: "Cancel",
645
+ editHistory: "Edit history",
646
+ loadingHistory: "Loading history...",
647
+ before: "Before",
648
+ after: "After",
649
+ warning: "Warning",
650
+ "delete": "Delete",
651
+ confirmDeleteComment: "Are you sure you want to delete this comment?",
652
+ confirmDeleteFile: "Are you sure you want to delete the file \"{{fileName}}\"?",
653
+ units: {
654
+ bytes: "B",
655
+ kilobytes: "KB",
656
+ megabytes: "MB"
657
+ },
658
+ errors: {
659
+ loadComments: "Failed to load comments.",
660
+ createComment: "Failed to create comment.",
661
+ updateComment: "Failed to update comment.",
662
+ loadEditHistory: "Failed to load edit history.",
663
+ uploadAttachment: "Failed to upload attachment.",
664
+ deleteComment: "Failed to delete comment.",
665
+ deleteAttachment: "Failed to delete attachment.",
666
+ downloadAttachment: "Failed to download attachment.",
667
+ updateReaction: "Failed to update reaction.",
668
+ addReaction: "Failed to add reaction."
669
+ }
670
+ };
671
+ var primeng$1 = "";
672
+ var en$6 = {
673
+ "app-configurator": {
674
+ primary: "Primary",
675
+ surface: "Surface",
676
+ presets: "Presets",
677
+ menuMode: "Menu Mode"
678
+ },
679
+ msgService: msgService$1,
680
+ securityComponent: securityComponent$1,
681
+ profileComponent: profileComponent$1,
682
+ configComponent: configComponent$1,
683
+ baseComponent: baseComponent$1,
684
+ menuComponent: menuComponent$1,
685
+ menuItemEditDialogComponent: menuItemEditDialogComponent$1,
686
+ menuItemCreateDialogComponent: menuItemCreateDialogComponent$1,
687
+ unauthorized: unauthorized$1,
688
+ menuItemComponent: menuItemComponent$1,
689
+ topbar: topbar$1,
690
+ userNotifications: userNotifications$1,
691
+ discussionComponent: discussionComponent$1,
692
+ primeng: primeng$1
693
+ };
694
+
695
+ var msgService = {
696
+ success: "Успешно",
697
+ info: "Информация",
698
+ warn: "Внимание",
699
+ error: "Ошибка",
700
+ secondary: "Вспомогательный"
701
+ };
702
+ var securityComponent = {
703
+ security: "Безопасность",
704
+ save: "Сохранить",
705
+ savedSecurity: "Настройки сохранены",
706
+ selectRoles: "Выберите роли"
707
+ };
708
+ var profileComponent = {
709
+ changePhoto: "Загрузить фото",
710
+ successfullyUploaded: "Загружено",
711
+ failedToLoadPhoto: "Не удалось загрузить фото пользователя"
712
+ };
713
+ var configComponent = {
714
+ profile: "Профиль",
715
+ photo: "Фото",
716
+ usePhoto256x256Pixel: "Используйте фото 256х256 пикселей",
717
+ localization: "Язык",
718
+ selectLanguage: "Выберите язык"
719
+ };
720
+ var baseComponent = {
721
+ content: "Содержимое",
722
+ settings: "Настройки",
723
+ security: "Безопасность",
724
+ success: "Сохранено"
725
+ };
726
+ var menuComponent = {
727
+ all: "Все",
728
+ "new": "Добавить"
729
+ };
730
+ var menuItemEditDialogComponent = {
731
+ header: "Редактировать элемент меню",
732
+ parentLabel: "Родительский узел",
733
+ label: "Имя",
734
+ module: "Модуль",
735
+ selectModule: "Выберите модуль",
736
+ icon: "Иконка",
737
+ cancel: "Отмена",
738
+ save: "Сохранить",
739
+ security: "Доступ на чтение"
740
+ };
741
+ var menuItemCreateDialogComponent = {
742
+ header: "Новый элемент меню",
743
+ parentLabel: "Родительский узел",
744
+ label: "Имя",
745
+ module: "Модуль",
746
+ selectModule: "Выберите модуль",
747
+ searchModule: "Поиск модуля",
748
+ icon: "Иконка",
749
+ cancel: "Отмена",
750
+ save: "Сохранить"
751
+ };
752
+ var unauthorized = {
753
+ welcomeToOip: "Добро пожаловать в OIP!",
754
+ signInToContinue: "Войдите чтобы продолжить",
755
+ signIn: "Войти"
756
+ };
757
+ var menuItemComponent = {
758
+ "new": "Добавить",
759
+ edit: "Редактировать",
760
+ "delete": "Удалить",
761
+ deleteItemConfirmHeader: "Удаление",
762
+ deleteItemConfirmMessage: "Вы уверены что хотите удалить элемент?",
763
+ deleteItemSuccessMessage: "Успешно удалено",
764
+ deleteItemConfirmRejectButtonPropsLabel: "Отмена",
765
+ deleteItemConfirmAcceptButtonPropsLabel: "Удалить",
766
+ moveUp: "Вверх",
767
+ moveDown: "Вниз"
768
+ };
769
+ var topbar = {
770
+ logout: "Выход",
771
+ logoutConfirmHeader: "Выход",
772
+ logoutConfirmMessage: "Вы уверены, что хотите выйти?",
773
+ logoutConfirmCancel: "Отмена",
774
+ logoutConfirmAccept: "Выйти",
775
+ profile: "Профиль",
776
+ applications: "Приложения"
777
+ };
778
+ var userNotifications = {
779
+ title: "Оповещения",
780
+ empty: "Непрочитанных оповещений нет",
781
+ refresh: "Обновить",
782
+ markAsRead: "Прочитано",
783
+ markedAsRead: "Оповещение отмечено прочитанным"
784
+ };
785
+ var discussionComponent = {
786
+ writeCommentPlaceholder: "Напишите комментарий. Используйте @email или @first.last для упоминаний.",
787
+ mentionSuggestions: "Варианты упоминаний",
788
+ edit: "Редактировать",
789
+ preview: "Предпросмотр",
790
+ attachFiles: "Прикрепить файлы",
791
+ send: "Отправить",
792
+ loadingComments: "Загрузка комментариев...",
793
+ noCommentsYet: "Комментариев пока нет.",
794
+ edited: "Изменено",
795
+ editCommentPlaceholder: "Изменить комментарий",
796
+ addAttachment: "Добавить вложение",
797
+ save: "Сохранить",
798
+ cancel: "Отмена",
799
+ editHistory: "История изменений",
800
+ loadingHistory: "Загрузка истории...",
801
+ before: "До",
802
+ after: "После",
803
+ warning: "Предупреждение",
804
+ "delete": "Удалить",
805
+ confirmDeleteComment: "Вы уверены, что хотите удалить этот комментарий?",
806
+ confirmDeleteFile: "Вы уверены, что хотите удалить файл \"{{fileName}}\"?",
807
+ units: {
808
+ bytes: "Б",
809
+ kilobytes: "КБ",
810
+ megabytes: "МБ"
811
+ },
812
+ errors: {
813
+ loadComments: "Не удалось загрузить комментарии.",
814
+ createComment: "Не удалось создать комментарий.",
815
+ updateComment: "Не удалось обновить комментарий.",
816
+ loadEditHistory: "Не удалось загрузить историю изменений.",
817
+ uploadAttachment: "Не удалось загрузить вложение.",
818
+ deleteComment: "Не удалось удалить комментарий.",
819
+ deleteAttachment: "Не удалось удалить вложение.",
820
+ downloadAttachment: "Не удалось скачать вложение.",
821
+ updateReaction: "Не удалось обновить реакцию.",
822
+ addReaction: "Не удалось добавить реакцию."
823
+ }
824
+ };
825
+ var primeng = {
826
+ accept: "Да",
827
+ addRule: "Добавить правило",
828
+ am: "до полудня",
829
+ apply: "Принять",
830
+ cancel: "Отмена",
831
+ choose: "Выбрать",
832
+ chooseDate: "Выбрать дату",
833
+ chooseMonth: "Выбрать месяц",
834
+ chooseYear: "Выбрать год",
835
+ clear: "Очистить",
836
+ completed: "Завершено",
837
+ contains: "Содержит",
838
+ custom: "Пользовательский",
839
+ dateAfter: "Дата после",
840
+ dateBefore: "Дата до",
841
+ dateFormat: "dd.mm.yy",
842
+ dateIs: "Дата равна",
843
+ dateIsNot: "Дата не равна",
844
+ dayNames: [
845
+ "Воскресенье",
846
+ "Понедельник",
847
+ "Вторник",
848
+ "Среда",
849
+ "Четверг",
850
+ "Пятница",
851
+ "Суббота"
852
+ ],
853
+ dayNamesMin: [
854
+ "Вс",
855
+ "Пн",
856
+ "Вт",
857
+ "Ср",
858
+ "Чт",
859
+ "Пт",
860
+ "Сб"
861
+ ],
862
+ dayNamesShort: [
863
+ "Вск",
864
+ "Пнд",
865
+ "Втр",
866
+ "Срд",
867
+ "Чтв",
868
+ "Птн",
869
+ "Сбт"
870
+ ],
871
+ emptyFilterMessage: "Результатов не найдено",
872
+ emptyMessage: "Нет доступных вариантов",
873
+ emptySearchMessage: "Результатов не найдено",
874
+ emptySelectionMessage: "Нет выбранного элемента",
875
+ endsWith: "Заканчивается",
876
+ equals: "Равно",
877
+ fileChosenMessage: "{0} файлов",
878
+ fileSizeTypes: [
879
+ "Б",
880
+ "Кб",
881
+ "Мб",
882
+ "Гб",
883
+ "Тб",
884
+ "Пб",
885
+ "Эб",
886
+ "Зб",
887
+ "Йб"
888
+ ],
889
+ filter: "Фильтр",
890
+ firstDayOfWeek: 1,
891
+ gt: "Более чем",
892
+ gte: "Более чем или равно",
893
+ lt: "Меньше чем",
894
+ lte: "Меньше чем или равно",
895
+ matchAll: "Сопоставить все",
896
+ matchAny: "Совпадение с любым",
897
+ medium: "Нормальный",
898
+ monthNames: [
899
+ "Январь",
900
+ "Февраль",
901
+ "Март",
902
+ "Апрель",
903
+ "Май",
904
+ "Июнь",
905
+ "Июль",
906
+ "Август",
907
+ "Сентябрь",
908
+ "Октябрь",
909
+ "Ноябрь",
910
+ "Декабрь"
911
+ ],
912
+ monthNamesShort: [
913
+ "Янв",
914
+ "Фев",
915
+ "Мар",
916
+ "Апр",
917
+ "Май",
918
+ "Июн",
919
+ "Июл",
920
+ "Авг",
921
+ "Сен",
922
+ "Окт",
923
+ "Ноя",
924
+ "Дек"
925
+ ],
926
+ nextDecade: "Следующее десятилетие",
927
+ nextHour: "Следующий час",
928
+ nextMinute: "Следующая минута",
929
+ nextMonth: "Следующий месяц",
930
+ nextSecond: "Следующая секунда",
931
+ nextYear: "Следующий год",
932
+ noFileChosenMessage: "Файл не выбран",
933
+ noFilter: "Нет фильтра",
934
+ notContains: "Не содержит",
935
+ notEquals: "Не равно",
936
+ now: "Сейчас",
937
+ passwordPrompt: "Введите пароль",
938
+ pending: "В ожидании",
939
+ pm: "после полудня",
940
+ prevDecade: "Предыдущее десятилетие",
941
+ prevHour: "Предыдущий час",
942
+ prevMinute: "Предыдущая минута",
943
+ prevMonth: "Предыдущий месяц",
944
+ prevSecond: "Предыдущая секунда",
945
+ prevYear: "Предыдущий год",
946
+ reject: "Нет",
947
+ removeRule: "Удалить правило",
948
+ searchMessage: "{0} результатов доступно",
949
+ selectionMessage: "{0} элементов выбрано",
950
+ showMonthAfterYear: false,
951
+ startsWith: "Начинается с",
952
+ strong: "Хороший",
953
+ today: "Сегодня",
954
+ upload: "Загрузить",
955
+ weak: "Простой",
956
+ weekHeader: "Нед.",
957
+ aria: {
958
+ cancelEdit: "Отменить правку",
959
+ close: "Закрыть",
960
+ collapseLabel: "Свернуть",
961
+ collapseRow: "Свернуть строку",
962
+ editRow: "Редактировать строку",
963
+ expandLabel: "Развернуть",
964
+ expandRow: "Развернуть строку",
965
+ falseLabel: "Неверно",
966
+ filterConstraint: "Ограничение фильтра",
967
+ filterOperator: "Оператор фильтра",
968
+ firstPageLabel: "Первая страница",
969
+ gridView: "В виде сетки",
970
+ hideFilterMenu: "Скрыть меню фильтра",
971
+ jumpToPageDropdownLabel: "Перейти к раскрывающемуся списку страниц",
972
+ jumpToPageInputLabel: "Перейти к вводу страницы",
973
+ lastPageLabel: "Последняя страница",
974
+ listLabel: "Список опций",
975
+ listView: "В виде списка",
976
+ moveAllToSource: "Переместить всё в источник",
977
+ moveAllToTarget: "Переместить всё в приёмник",
978
+ moveBottom: "Переместить в конец",
979
+ moveDown: "Переместить вниз",
980
+ moveTop: "Переместить в начало",
981
+ moveToSource: "Переместить в источник",
982
+ moveToTarget: "Переместить в приёмник",
983
+ moveUp: "Переместить вверх",
984
+ navigation: "Навигация",
985
+ next: "Следующий",
986
+ nextPageLabel: "Следующая страница",
987
+ nullLabel: "Не выбран",
988
+ otpLabel: "Введите символ одноразового пароля {0}",
989
+ pageLabel: "{page}",
990
+ passwordHide: "Скрыть пароль",
991
+ passwordShow: "Показать пароль",
992
+ previous: "Предыдущий",
993
+ prevPageLabel: "Предыдущая страница",
994
+ removeLabel: "Удалить",
995
+ rotateLeft: "Повернуть влево",
996
+ rotateRight: "Повернуть вправо",
997
+ rowsPerPageLabel: "Строк на странице",
998
+ saveEdit: "Сохранить правку",
999
+ scrollTop: "Прокрутить в начало",
1000
+ selectAll: "Выбрать все строки",
1001
+ selectLabel: "Выбрать",
1002
+ selectRow: "Выбрать строку",
1003
+ showFilterMenu: "Показать меню фильтра",
1004
+ slide: "Слайд",
1005
+ slideNumber: "{slideNumber}",
1006
+ star: "1 звезда",
1007
+ stars: "{star} звёзд",
1008
+ trueLabel: "Верно",
1009
+ unselectAll: "Отменить выбор всех строк",
1010
+ unselectLabel: "Отменить выбор",
1011
+ unselectRow: "Отменить выбор строки",
1012
+ zoomImage: "Увеличить изображение",
1013
+ zoomIn: "Увеличить",
1014
+ zoomOut: "Уменьшить"
1015
+ }
1016
+ };
1017
+ var ru$6 = {
1018
+ "app-configurator": {
1019
+ primary: "Основной",
1020
+ surface: "Фон",
1021
+ presets: "Стиль",
1022
+ menuMode: "Режим меню"
1023
+ },
1024
+ msgService: msgService,
1025
+ securityComponent: securityComponent,
1026
+ profileComponent: profileComponent,
1027
+ configComponent: configComponent,
1028
+ baseComponent: baseComponent,
1029
+ menuComponent: menuComponent,
1030
+ menuItemEditDialogComponent: menuItemEditDialogComponent,
1031
+ menuItemCreateDialogComponent: menuItemCreateDialogComponent,
1032
+ unauthorized: unauthorized,
1033
+ menuItemComponent: menuItemComponent,
1034
+ topbar: topbar,
1035
+ userNotifications: userNotifications,
1036
+ discussionComponent: discussionComponent,
1037
+ primeng: primeng
1038
+ };
1039
+
1040
+ /**
1041
+ * Global dictionaries shared by all components. They are bundled with the library,
1042
+ * so the application never requests `assets/i18n/{lang}.json`.
1043
+ */
1044
+ const globalTranslations = { en: en$6, ru: ru$6 };
525
1045
  /**
526
1046
  * Service for managing translation loading in the application
527
1047
  */
528
1048
  class L10nService {
1049
+ /**
1050
+ * Translations bundled with components, registered at module load time.
1051
+ */
1052
+ static { this.staticTranslations = new Map(); }
1053
+ /**
1054
+ * Registers translations bundled with a component instead of loading them over HTTP.
1055
+ * Namespaces are taken from the root keys of the passed dictionaries.
1056
+ * Components derived from <c>BaseModuleComponent</c> don't call it directly - it is enough
1057
+ * to declare the static <c>translations</c> field.
1058
+ * @param byLang - Translations grouped by language code
1059
+ * @param namespace - Explicit namespace, needed when the dictionaries are still empty
1060
+ */
1061
+ static registerTranslations(byLang, namespace) {
1062
+ if (!byLang) {
1063
+ return;
1064
+ }
1065
+ if (namespace) {
1066
+ L10nService.staticTranslations.set(namespace, byLang);
1067
+ }
1068
+ for (const translations of Object.values(byLang)) {
1069
+ for (const namespace of Object.keys(translations)) {
1070
+ L10nService.staticTranslations.set(namespace, byLang);
1071
+ }
1072
+ }
1073
+ }
529
1074
  constructor() {
530
1075
  this.loadedTranslations = new Set();
531
1076
  this.loadingTranslations = new Map();
@@ -533,6 +1078,18 @@ class L10nService {
533
1078
  this.translateService = inject(TranslateService);
534
1079
  this.primeNg = inject(PrimeNG);
535
1080
  this.layoutService = inject(LayoutService);
1081
+ // Global dictionaries are available before the first `use()` call, so TranslateHttpLoader is never asked for them.
1082
+ L10nService.registerTranslations(globalTranslations);
1083
+ Object.entries(globalTranslations).forEach(([lang, translations]) => this.mergeTranslation(lang, translations));
1084
+ // Static translations are merged once per language, so they have to be re-merged on every language change.
1085
+ this.translateService.onLangChange.subscribe((event) => {
1086
+ L10nService.staticTranslations.forEach((byLang) => {
1087
+ const translations = byLang[event.lang];
1088
+ if (translations) {
1089
+ this.mergeTranslation(event.lang, translations);
1090
+ }
1091
+ });
1092
+ });
536
1093
  }
537
1094
  /**
538
1095
  * Loads translations for a specific component
@@ -542,6 +1099,35 @@ class L10nService {
542
1099
  const lang = this.translateService.currentLang || this.layoutService.language() || 'en';
543
1100
  return this.loadTranslations(component, lang);
544
1101
  }
1102
+ /**
1103
+ * Loads translations from an explicit asset URL and merges them into the active language dictionary.
1104
+ * Use this for extension modules whose assets are hosted outside the shell application.
1105
+ */
1106
+ loadTranslationsFromUrl(namespace, url, lang) {
1107
+ const selectedLang = lang || this.translateService.currentLang || this.layoutService.language() || 'en';
1108
+ const key = `${namespace}.${selectedLang}.${url}`;
1109
+ if (this.loadedTranslations.has(key)) {
1110
+ return of(null);
1111
+ }
1112
+ const loading = this.loadingTranslations.get(key);
1113
+ if (loading) {
1114
+ return loading;
1115
+ }
1116
+ const request = this.httpClient.get(url).pipe(tap((translations) => {
1117
+ this.mergeTranslation(selectedLang, translations);
1118
+ this.loadedTranslations.add(key);
1119
+ this.loadingTranslations.delete(key);
1120
+ }), shareReplay(1));
1121
+ this.loadingTranslations.set(key, request);
1122
+ request.subscribe({
1123
+ error: (e) => {
1124
+ this.loadingTranslations.delete(key);
1125
+ console.error(`No translations found for ${namespace}.${selectedLang}.json at ${url}`);
1126
+ console.error(e);
1127
+ }
1128
+ });
1129
+ return request;
1130
+ }
545
1131
  /**
546
1132
  * Gets the translated value of a key (or an array of keys)
547
1133
  * @returns the translated key, or an object of translated keys
@@ -560,13 +1146,21 @@ class L10nService {
560
1146
  if (this.loadedTranslations.has(key)) {
561
1147
  return of(null);
562
1148
  }
1149
+ // Translations bundled with the component - no HTTP request needed.
1150
+ const staticTranslations = L10nService.staticTranslations.get(component);
1151
+ if (staticTranslations) {
1152
+ const translations = staticTranslations[lang];
1153
+ if (translations) {
1154
+ this.mergeTranslation(lang, translations);
1155
+ }
1156
+ return of(null);
1157
+ }
563
1158
  const loading = this.loadingTranslations.get(key);
564
1159
  if (loading) {
565
1160
  return loading;
566
1161
  }
567
1162
  const request = this.httpClient.get(`./assets/i18n/${component}.${lang}.json`).pipe(tap((translations) => {
568
- const current = this.translateService.translations[lang] || {};
569
- this.translateService.setTranslation(lang, { ...current, ...translations }, true);
1163
+ this.mergeTranslation(lang, translations);
570
1164
  this.loadedTranslations.add(key);
571
1165
  this.loadingTranslations.delete(key);
572
1166
  }), shareReplay(1));
@@ -580,6 +1174,15 @@ class L10nService {
580
1174
  });
581
1175
  return request;
582
1176
  }
1177
+ /**
1178
+ * Merges a translation dictionary into the dictionary of the given language
1179
+ * @param lang - Language code
1180
+ * @param translations - Translations to merge
1181
+ */
1182
+ mergeTranslation(lang, translations) {
1183
+ const current = this.translateService.translations[lang] || {};
1184
+ this.translateService.setTranslation(lang, { ...current, ...translations }, true);
1185
+ }
583
1186
  /**
584
1187
  * Changes the lang currently used
585
1188
  */
@@ -608,7 +1211,7 @@ class L10nService {
608
1211
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: L10nService, decorators: [{
609
1212
  type: Injectable,
610
1213
  args: [{ providedIn: 'root' }]
611
- }] });
1214
+ }], ctorParameters: () => [] });
612
1215
 
613
1216
  class SecurityService {
614
1217
  }
@@ -620,16 +1223,21 @@ class BffSecurityService {
620
1223
  this.csrfToken = new ReplaySubject(1);
621
1224
  this.payload = new BehaviorSubject(null);
622
1225
  }
1226
+ static { this.authorizeAfterLogoutReturnUrlKey = 'oip.authorizeAfterLogoutReturnUrl'; }
623
1227
  auth() {
624
- this.http.get(this.buildUrl('api/security/get-current-auth-session'), {
1228
+ this.http
1229
+ .get(this.buildUrl('api/security/get-current-auth-session'), {
625
1230
  withCredentials: true
626
- }).pipe(tap$1((session) => this.applySession(session)), catchError(() => {
1231
+ })
1232
+ .pipe(tap$1((session) => this.applySession(session)), catchError(() => {
627
1233
  this.applyAnonymousSession();
628
1234
  return of(null);
629
- })).subscribe();
1235
+ }))
1236
+ .subscribe();
630
1237
  }
631
1238
  logout() {
632
1239
  firstValueFrom(this.getCsrfToken()).then((csrfToken) => {
1240
+ this.storeAuthorizeAfterLogoutReturnUrl();
633
1241
  const form = this.createPostForm(this.buildUrl('api/security/delete-auth-session'));
634
1242
  if (csrfToken?.token) {
635
1243
  const tokenInput = document.createElement('input');
@@ -665,15 +1273,20 @@ class BffSecurityService {
665
1273
  });
666
1274
  }
667
1275
  getCsrfToken() {
668
- this.http.get(this.buildUrl('api/security/get-auth-csrf-token'), {
1276
+ this.http
1277
+ .get(this.buildUrl('api/security/get-auth-csrf-token'), {
669
1278
  withCredentials: true
670
- }).pipe(catchError(() => of(null))).subscribe((token) => this.csrfToken.next(token));
1279
+ })
1280
+ .pipe(catchError(() => of(null)))
1281
+ .subscribe((token) => this.csrfToken.next(token));
671
1282
  return this.csrfToken.asObservable();
672
1283
  }
673
1284
  isAdmin() {
674
1285
  return this.payload.getValue()?.realm_access?.roles?.includes('admin') ?? false;
675
1286
  }
676
- authorize() {
1287
+ authorize(returnUrl) {
1288
+ const normalizedReturnUrl = this.normalizeReturnUrl(returnUrl);
1289
+ this.replaceWithUnauthorizedUrl(normalizedReturnUrl);
677
1290
  const form = this.createPostForm(this.buildUrl('api/security/create-auth-session'));
678
1291
  document.body.appendChild(form);
679
1292
  form.submit();
@@ -724,10 +1337,40 @@ class BffSecurityService {
724
1337
  form.style.display = 'none';
725
1338
  return form;
726
1339
  }
1340
+ normalizeReturnUrl(returnUrl) {
1341
+ const candidate = returnUrl?.trim();
1342
+ if (!candidate || /[\u0000-\u001f\u007f\\]/.test(candidate)) {
1343
+ return '/';
1344
+ }
1345
+ if (!candidate.startsWith('/') || candidate.startsWith('//')) {
1346
+ return '/';
1347
+ }
1348
+ try {
1349
+ const url = new URL(candidate, window.location.origin);
1350
+ if (url.origin !== window.location.origin) {
1351
+ return '/';
1352
+ }
1353
+ const normalizedUrl = `${url.pathname}${url.search}${url.hash}`;
1354
+ return normalizedUrl.startsWith('/unauthorized') ? '/' : normalizedUrl;
1355
+ }
1356
+ catch {
1357
+ return '/';
1358
+ }
1359
+ }
1360
+ replaceWithUnauthorizedUrl(returnUrl) {
1361
+ const unauthorizedUrl = new URL('unauthorized', this.getBaseUrl());
1362
+ unauthorizedUrl.searchParams.set('returnUrl', returnUrl);
1363
+ history.replaceState(history.state, '', `${unauthorizedUrl.pathname}${unauthorizedUrl.search}`);
1364
+ }
1365
+ storeAuthorizeAfterLogoutReturnUrl() {
1366
+ sessionStorage.setItem(BffSecurityService.authorizeAfterLogoutReturnUrlKey, this.normalizeReturnUrl(`${window.location.pathname}${window.location.search}${window.location.hash}`));
1367
+ }
727
1368
  buildUrl(path) {
1369
+ return `${this.getBaseUrl()}${path}`;
1370
+ }
1371
+ getBaseUrl() {
728
1372
  const baseUrl = document.getElementsByTagName('base')[0].href;
729
- const normalizedBase = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
730
- return `${normalizedBase}${path}`;
1373
+ return baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;
731
1374
  }
732
1375
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: BffSecurityService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
733
1376
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: BffSecurityService }); }
@@ -751,15 +1394,22 @@ class HttpClient {
751
1394
  constructor() {
752
1395
  this.securityService = inject(SecurityService);
753
1396
  this.layoutService = inject(LayoutService);
1397
+ this.router = inject(Router);
754
1398
  this.baseUrl = "";
755
- this.securityWorker = () => ({
756
- headers: {
757
- "Accept-language": this.layoutService.language()
758
- ? this.layoutService.language()
759
- : "en",
760
- "X-Timezone": this.layoutService.timeZone(),
761
- },
762
- });
1399
+ this.securityWorker = () => {
1400
+ const moduleInstanceId = this.getCurrentModuleInstanceId();
1401
+ return {
1402
+ headers: {
1403
+ "Accept-language": this.layoutService.language()
1404
+ ? this.layoutService.language()
1405
+ : "en",
1406
+ "X-Timezone": this.layoutService.timeZone(),
1407
+ ...(moduleInstanceId != null
1408
+ ? { "X-Module-Instance-Id": String(moduleInstanceId) }
1409
+ : {}),
1410
+ },
1411
+ };
1412
+ };
763
1413
  this.abortControllers = new Map();
764
1414
  this.customFetch = (...fetchParams) => fetch(...fetchParams);
765
1415
  this.baseApiParams = {
@@ -864,12 +1514,29 @@ class HttpClient {
864
1514
  if (cancelToken) {
865
1515
  this.abortControllers.delete(cancelToken);
866
1516
  }
867
- if (!response.ok)
1517
+ if (!response.ok) {
1518
+ this.authorizeOnUnauthorized(response, path);
868
1519
  throw data;
1520
+ }
869
1521
  return data.data;
870
1522
  });
871
1523
  };
872
1524
  }
1525
+ /**
1526
+ * Reads the module instance id of the deepest activated route.
1527
+ * The backend uses it to check module instance rights on endpoints that do not carry the id in their contract.
1528
+ */
1529
+ getCurrentModuleInstanceId() {
1530
+ // May run before the first navigation completes, when route snapshots are not available yet.
1531
+ let route = this.router.routerState?.root;
1532
+ let id = null;
1533
+ while (route) {
1534
+ id = route.snapshot?.paramMap?.get("id") ?? id;
1535
+ route = route.firstChild;
1536
+ }
1537
+ const parsed = id != null ? Number(id) : Number.NaN;
1538
+ return Number.isFinite(parsed) ? parsed : undefined;
1539
+ }
873
1540
  encodeQueryParam(key, value) {
874
1541
  const encodedKey = encodeURIComponent(key);
875
1542
  return `${encodedKey}=${encodeURIComponent(typeof value === "number" ? value : `${value}`)}`;
@@ -906,6 +1573,13 @@ class HttpClient {
906
1573
  },
907
1574
  };
908
1575
  }
1576
+ authorizeOnUnauthorized(response, path) {
1577
+ if (response.status !== 401 ||
1578
+ path.includes("/api/security/create-auth-session")) {
1579
+ return;
1580
+ }
1581
+ this.securityService.authorize(`${window.location.pathname}${window.location.search}${window.location.hash}`);
1582
+ }
909
1583
  async getCsrfRequestParams(method, path) {
910
1584
  if (!method ||
911
1585
  !["POST", "PUT", "PATCH", "DELETE"].includes(method.toUpperCase())) {
@@ -1077,6 +1751,8 @@ class BaseModuleComponent {
1077
1751
  { id: 'settings', icon: 'pi-cog', caption: '' },
1078
1752
  { id: 'security', icon: 'pi-lock', caption: '' }
1079
1753
  ];
1754
+ // Must run before the route subscription below asks L10nService for the translations.
1755
+ L10nService.registerTranslations(this.constructor.translations);
1080
1756
  effect(() => {
1081
1757
  const config = this.localSettings();
1082
1758
  if (config) {
@@ -1085,6 +1761,8 @@ class BaseModuleComponent {
1085
1761
  });
1086
1762
  this.subscriptions.push(this.route.url.subscribe((url) => {
1087
1763
  this.controller = url[0].path;
1764
+ // The route path is the translation namespace, register it before asking for translations.
1765
+ L10nService.registerTranslations(this.constructor.translations, this.controller);
1088
1766
  this.l10n$ = this.l10nService.get(this.controller);
1089
1767
  }));
1090
1768
  this.subscriptions.push(this.route.paramMap.subscribe((params) => {
@@ -1185,20 +1863,31 @@ class BaseModuleComponent {
1185
1863
  this.rightsSubscription?.unsubscribe();
1186
1864
  this.resetRightsState();
1187
1865
  if (!controller || id == null) {
1188
- return;
1189
- }
1190
- this.rightsSubscription = this.securityService.payload
1191
- .pipe(switchMap((payload) => from(this.getSecurity(controller, id)).pipe(map((securitySettings) => ({ payload, securitySettings })))), takeUntilDestroyed(this.destroyRef))
1192
- .subscribe({
1193
- next: ({ payload, securitySettings }) => {
1194
- const roles = payload?.realm_access?.roles ?? [];
1195
- this.updateRightsState(roles, securitySettings);
1196
- },
1197
- error: (error) => {
1198
- this.securityRightsLoaded = true;
1199
- console.error('Не удалось загрузить права', error);
1200
- this.onSecurityRightsChange();
1201
- }
1866
+ return Promise.resolve();
1867
+ }
1868
+ return new Promise((resolve) => {
1869
+ let settled = false;
1870
+ const settle = () => {
1871
+ if (!settled) {
1872
+ settled = true;
1873
+ resolve();
1874
+ }
1875
+ };
1876
+ this.rightsSubscription = this.securityService.payload
1877
+ .pipe(switchMap((payload) => from(this.getSecurity(controller, id)).pipe(map((securitySettings) => ({ payload, securitySettings })))), takeUntilDestroyed(this.destroyRef))
1878
+ .subscribe({
1879
+ next: ({ payload, securitySettings }) => {
1880
+ const roles = payload?.realm_access?.roles ?? [];
1881
+ this.updateRightsState(roles, securitySettings);
1882
+ settle();
1883
+ },
1884
+ error: (error) => {
1885
+ this.securityRightsLoaded = true;
1886
+ console.error('Не удалось загрузить права', error);
1887
+ this.onSecurityRightsChange();
1888
+ settle();
1889
+ }
1890
+ });
1202
1891
  });
1203
1892
  }
1204
1893
  resetRightsState() {
@@ -1219,9 +1908,7 @@ class BaseModuleComponent {
1219
1908
  this.onSecurityRightsChange();
1220
1909
  }
1221
1910
  hasSecurityRight(roles, securitySettings, code) {
1222
- return securitySettings
1223
- .find((security) => security.code === code)
1224
- ?.roles?.some((role) => roles.includes(role)) ?? false;
1911
+ return (securitySettings.find((security) => security.code === code)?.roles?.some((role) => roles.includes(role)) ?? false);
1225
1912
  }
1226
1913
  getSecurity(controller = this.controller, id = this.id) {
1227
1914
  if (!controller || id == null) {
@@ -1230,7 +1917,6 @@ class BaseModuleComponent {
1230
1917
  return this.httpClient.request({
1231
1918
  path: `/api/${controller}/get-security`,
1232
1919
  method: 'GET',
1233
- query: { id },
1234
1920
  secure: true,
1235
1921
  format: 'json'
1236
1922
  });
@@ -1244,11 +1930,10 @@ class BaseModuleComponent {
1244
1930
  type: ContentType.Json
1245
1931
  });
1246
1932
  }
1247
- getModuleInstanceSettings(controller = this.controller, id = this.id) {
1933
+ getModuleInstanceSettings(controller = this.controller) {
1248
1934
  return this.httpClient.request({
1249
1935
  path: `/api/${controller}/get-module-instance-settings`,
1250
1936
  method: 'GET',
1251
- query: { id },
1252
1937
  secure: true,
1253
1938
  format: 'json'
1254
1939
  });
@@ -1281,8 +1966,12 @@ class BaseModuleComponent {
1281
1966
  }
1282
1967
  async reloadModuleInstance() {
1283
1968
  this.moduleInstanceReloadPromise = this.moduleInstanceReloadPromise.then(async () => {
1969
+ // Rights first: the backend rejects settings and data requests without the read right.
1970
+ await this.watchSecurityRights();
1971
+ if (!this.canRead) {
1972
+ return;
1973
+ }
1284
1974
  await this.getSettings();
1285
- this.watchSecurityRights();
1286
1975
  await this.onModuleInstanceChange();
1287
1976
  });
1288
1977
  await this.moduleInstanceReloadPromise;
@@ -1396,7 +2085,7 @@ class SecurityComponent {
1396
2085
  return;
1397
2086
  }
1398
2087
  try {
1399
- const result = await this.getSecurity(controller, id);
2088
+ const result = await this.getSecurity(controller);
1400
2089
  if (loadToken === this.securityLoadToken) {
1401
2090
  this.securityData = result;
1402
2091
  }
@@ -1407,11 +2096,10 @@ class SecurityComponent {
1407
2096
  }
1408
2097
  }
1409
2098
  }
1410
- getSecurity(controller, id) {
2099
+ getSecurity(controller) {
1411
2100
  return this.httpClient.request({
1412
2101
  path: `/api/${controller}/get-security`,
1413
2102
  method: 'GET',
1414
- query: { id },
1415
2103
  secure: true,
1416
2104
  format: 'json'
1417
2105
  });
@@ -1548,8 +2236,7 @@ class AppConfiguratorComponent {
1548
2236
  this.themePresets = this.getThemePresets();
1549
2237
  this.themePresetsMap = new Map(this.themePresets.map((theme) => [theme.id, theme]));
1550
2238
  this.defaultThemePreset = this.themePresets[0] ?? DEFAULT_THEME_PRESETS[0];
1551
- this.fallbackPrimaryColors = (DEFAULT_THEME_PRESETS[0].preset
1552
- .primitive ?? {});
2239
+ this.fallbackPrimaryColors = (DEFAULT_THEME_PRESETS[0].preset.primitive ?? {});
1553
2240
  this.presets = this.themePresets.map((theme) => ({ label: theme.label ?? theme.id, value: theme.id }));
1554
2241
  this.showMenuModeButton = signal(!this.router.url.includes('auth'), ...(ngDevMode ? [{ debugName: "showMenuModeButton" }] : []));
1555
2242
  this.menuModeOptions = [
@@ -1868,7 +2555,7 @@ class AppConfiguratorComponent {
1868
2555
  .filter((entry) => Boolean(entry[1]))
1869
2556
  .map(([name, palette]) => ({ name, palette }));
1870
2557
  }
1871
- const presetPalette = (activeThemePreset.preset.primitive ?? {});
2558
+ const presetPalette = activeThemePreset.preset.primitive ?? {};
1872
2559
  const palettes = [{ name: 'noir', palette: {} }];
1873
2560
  PRIMARY_COLORS.forEach((color) => {
1874
2561
  palettes.push({
@@ -1927,7 +2614,9 @@ class AppConfiguratorComponent {
1927
2614
  this.layoutService.layoutConfig.update((state) => ({
1928
2615
  ...state,
1929
2616
  preset: nextThemeId,
1930
- primary: primaryColors.some((color) => color.name === state.primary) ? state.primary : (primaryColors[0]?.name ?? state.primary),
2617
+ primary: primaryColors.some((color) => color.name === state.primary)
2618
+ ? state.primary
2619
+ : (primaryColors[0]?.name ?? state.primary),
1931
2620
  surface: nextTheme.surfaceColors && !surfaceColors.some((color) => color.name === state.surface)
1932
2621
  ? (surfaceColors[0]?.name ?? state.surface)
1933
2622
  : state.surface
@@ -1991,12 +2680,12 @@ class AppConfiguratorComponent {
1991
2680
  <span class="text-sm text-muted-color font-semibold">{{ 'app-configurator.presets' | translate }}</span>
1992
2681
  <p-selectButton
1993
2682
  id="oip-app-configurator-preset-select-button"
2683
+ optionLabel="label"
2684
+ optionValue="value"
1994
2685
  size="small"
1995
2686
  [allowEmpty]="false"
1996
2687
  [ngModel]="selectedPreset()"
1997
2688
  [options]="presets"
1998
- optionLabel="label"
1999
- optionValue="value"
2000
2689
  (ngModelChange)="onPresetChange($event)" />
2001
2690
  </div>
2002
2691
  @if (showMenuModeButton()) {
@@ -2068,12 +2757,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
2068
2757
  <span class="text-sm text-muted-color font-semibold">{{ 'app-configurator.presets' | translate }}</span>
2069
2758
  <p-selectButton
2070
2759
  id="oip-app-configurator-preset-select-button"
2760
+ optionLabel="label"
2761
+ optionValue="value"
2071
2762
  size="small"
2072
2763
  [allowEmpty]="false"
2073
2764
  [ngModel]="selectedPreset()"
2074
2765
  [options]="presets"
2075
- optionLabel="label"
2076
- optionValue="value"
2077
2766
  (ngModelChange)="onPresetChange($event)" />
2078
2767
  </div>
2079
2768
  @if (showMenuModeButton()) {
@@ -2102,10 +2791,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
2102
2791
  class UserProfileApi extends HttpClient {
2103
2792
  constructor() {
2104
2793
  super(...arguments);
2105
- this.getUserPhoto = (query, params = {}) => this.request({
2794
+ this.getUserPhoto = (params = {}) => this.request({
2106
2795
  path: `/api/user-profile/get-user-photo`,
2107
2796
  method: "GET",
2108
- query: query,
2797
+ secure: true,
2798
+ format: "blob",
2799
+ ...params,
2800
+ });
2801
+ this.getUserPhotoById = ({ userId, ...query }, params = {}) => this.request({
2802
+ path: `/api/user-profile/get-user-photo-by-id/${userId}`,
2803
+ method: "GET",
2109
2804
  secure: true,
2110
2805
  format: "blob",
2111
2806
  ...params,
@@ -2149,7 +2844,9 @@ class UserService {
2149
2844
  constructor() {
2150
2845
  this.securityService = inject(SecurityService);
2151
2846
  this.userProfileApi = inject(UserProfileApi);
2152
- this.requestedPhotoEmail = null;
2847
+ this.msgService = inject(MsgService);
2848
+ this.translateService = inject(TranslateService);
2849
+ this.requestedPhotoKey = null;
2153
2850
  /**
2154
2851
  * Stores the user's photo as a data URL or binary blob, depending on how it's processed.
2155
2852
  */
@@ -2160,7 +2857,7 @@ class UserService {
2160
2857
  this.photoLoaded = false;
2161
2858
  this.securityService
2162
2859
  .getCurrentUser$()
2163
- .pipe(map((user) => user?.email ?? null), filter((email) => !!email), distinctUntilChanged())
2860
+ .pipe(map((user) => this.getCurrentUserPhotoKey(user)), filter((photoKey) => !!photoKey), distinctUntilChanged())
2164
2861
  .subscribe(() => {
2165
2862
  this.getUserPhoto();
2166
2863
  });
@@ -2178,35 +2875,41 @@ class UserService {
2178
2875
  }
2179
2876
  get userName() {
2180
2877
  const data = this.securityService.getCurrentUser();
2181
- return [data?.given_name, data?.family_name].filter(Boolean).join(' ')
2182
- || data?.displayName
2183
- || data?.name
2184
- || data?.userName
2185
- || data?.preferred_username
2186
- || data?.email
2187
- || '';
2878
+ return ([data?.given_name, data?.family_name].filter(Boolean).join(' ') ||
2879
+ data?.displayName ||
2880
+ data?.name ||
2881
+ data?.userName ||
2882
+ data?.preferred_username ||
2883
+ data?.email ||
2884
+ '');
2188
2885
  }
2189
2886
  /**
2190
- * Initiates an HTTP request to fetch the user's photo based on their email,
2887
+ * Initiates an HTTP request to fetch the current user's photo,
2191
2888
  * and updates the `photo` and `photoLoaded` properties accordingly.
2192
2889
  */
2193
2890
  getUserPhoto() {
2194
- const email = this.securityService.getCurrentUser()?.email;
2195
- if (!email) {
2891
+ const photoKey = this.getCurrentUserPhotoKey(this.securityService.getCurrentUser());
2892
+ if (!photoKey) {
2196
2893
  return;
2197
2894
  }
2198
- if (this.requestedPhotoEmail === email && (this.photoLoaded || this.photo)) {
2895
+ if (this.requestedPhotoKey === photoKey && (this.photoLoaded || this.photo)) {
2199
2896
  return;
2200
2897
  }
2201
- this.requestedPhotoEmail = email;
2202
- this.userProfileApi.getUserPhoto({ email }, { format: 'blob' }).then((data) => {
2898
+ this.requestedPhotoKey = photoKey;
2899
+ this.userProfileApi.getUserPhoto({ format: 'blob' }).then((data) => {
2203
2900
  this.createImageFromBlob(data);
2204
2901
  this.photoLoaded = true;
2205
2902
  }, (error) => {
2206
- console.log(error);
2903
+ this.msgService.errorFromException(error, this.translateService.instant('profileComponent.failedToLoadPhoto'));
2207
2904
  this.photoLoaded = false;
2208
2905
  });
2209
2906
  }
2907
+ refreshUserPhoto() {
2908
+ this.requestedPhotoKey = null;
2909
+ this.photo = null;
2910
+ this.photoLoaded = false;
2911
+ this.getUserPhoto();
2912
+ }
2210
2913
  /**
2211
2914
  * Converts a Blob image into a Base64 data URL and stores it in the `photo` property.
2212
2915
  *
@@ -2222,14 +2925,17 @@ class UserService {
2222
2925
  }
2223
2926
  }
2224
2927
  getInitials(value) {
2225
- return value
2928
+ return (value
2226
2929
  ?.trim()
2227
2930
  .split(/\s+/)
2228
2931
  .filter(Boolean)
2229
2932
  .slice(0, 2)
2230
2933
  .map((part) => part[0])
2231
2934
  .join('')
2232
- .toUpperCase() ?? '';
2935
+ .toUpperCase() ?? '');
2936
+ }
2937
+ getCurrentUserPhotoKey(user) {
2938
+ return user?.sub || user?.userName || user?.preferred_username || user?.displayName || user?.name || null;
2233
2939
  }
2234
2940
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: UserService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2235
2941
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: UserService }); }
@@ -2502,8 +3208,8 @@ class UserNotificationsComponent {
2502
3208
  class="layout-topbar-action relative"
2503
3209
  id="oip-app-topbar-notification-button"
2504
3210
  type="button"
2505
- (click)="toggle($event)"
2506
- [attr.aria-label]="'Notifications: ' + unreadNotificationCount()">
3211
+ [attr.aria-label]="'Notifications: ' + unreadNotificationCount()"
3212
+ (click)="toggle($event)">
2507
3213
  <i class="pi pi-bell"></i>
2508
3214
  @if (unreadNotificationCount() > 0) {
2509
3215
  <p-badge
@@ -2576,11 +3282,11 @@ class UserNotificationsComponent {
2576
3282
  </div>
2577
3283
 
2578
3284
  <p-paginator
2579
- styleClass="mt-3"
3285
+ class="mt-3"
2580
3286
  [first]="skip"
2581
3287
  [rows]="take"
2582
- [totalRecords]="totalCount"
2583
3288
  [rowsPerPageOptions]="[5, 10, 20]"
3289
+ [totalRecords]="totalCount"
2584
3290
  (onPageChange)="onPageChange($event)"></p-paginator>
2585
3291
  }
2586
3292
  </p-popover>
@@ -2591,14 +3297,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
2591
3297
  args: [{
2592
3298
  selector: 'app-user-notifications',
2593
3299
  standalone: true,
2594
- imports: [CommonModule, BadgeModule, ButtonModule, PaginatorModule, Popover, ProgressSpinnerModule, Tag, Tooltip, TranslatePipe],
3300
+ imports: [
3301
+ CommonModule,
3302
+ BadgeModule,
3303
+ ButtonModule,
3304
+ PaginatorModule,
3305
+ Popover,
3306
+ ProgressSpinnerModule,
3307
+ Tag,
3308
+ Tooltip,
3309
+ TranslatePipe
3310
+ ],
2595
3311
  template: `
2596
3312
  <button
2597
3313
  class="layout-topbar-action relative"
2598
3314
  id="oip-app-topbar-notification-button"
2599
3315
  type="button"
2600
- (click)="toggle($event)"
2601
- [attr.aria-label]="'Notifications: ' + unreadNotificationCount()">
3316
+ [attr.aria-label]="'Notifications: ' + unreadNotificationCount()"
3317
+ (click)="toggle($event)">
2602
3318
  <i class="pi pi-bell"></i>
2603
3319
  @if (unreadNotificationCount() > 0) {
2604
3320
  <p-badge
@@ -2671,11 +3387,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
2671
3387
  </div>
2672
3388
 
2673
3389
  <p-paginator
2674
- styleClass="mt-3"
3390
+ class="mt-3"
2675
3391
  [first]="skip"
2676
3392
  [rows]="take"
2677
- [totalRecords]="totalCount"
2678
3393
  [rowsPerPageOptions]="[5, 10, 20]"
3394
+ [totalRecords]="totalCount"
2679
3395
  (onPageChange)="onPageChange($event)"></p-paginator>
2680
3396
  }
2681
3397
  </p-popover>
@@ -2839,6 +3555,10 @@ class AppTopbar {
2839
3555
  this.confirmationService = inject(ConfirmationService);
2840
3556
  this.translateService = inject(TranslateService);
2841
3557
  }
3558
+ onActiveTabChange(value) {
3559
+ const nextActiveId = value == null ? undefined : String(value);
3560
+ this.topBarService.activeId = nextActiveId;
3561
+ }
2842
3562
  toggleDarkMode() {
2843
3563
  this.layoutService.layoutConfig.update((state) => ({
2844
3564
  ...state,
@@ -2871,106 +3591,108 @@ class AppTopbar {
2871
3591
  });
2872
3592
  }
2873
3593
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: AppTopbar, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2874
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.24", type: AppTopbar, isStandalone: true, selector: "app-topbar", providers: [ConfirmationService], ngImport: i0, template: `
2875
- <div class="layout-topbar">
2876
- <p-confirmDialog appendTo="body"/>
2877
- <div class="layout-topbar-logo-container">
2878
- <button class="layout-menu-button layout-topbar-action" (click)="layoutService.onMenuToggle()">
2879
- <i class="pi pi-bars"></i>
2880
- </button>
2881
- <a class="layout-topbar-logo" id="oip-app-topbar-logo-link" routerLink="">
2882
- <ng-container
2883
- *ngComponentOutlet="logoService.getLogoComponent(); inputs: { width: 36, height: 36 }"></ng-container>
2884
- <span>{{ 'app-info.title' | translate }}</span>
2885
- </a>
2886
- </div>
3594
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.24", type: AppTopbar, isStandalone: true, selector: "app-topbar", providers: [ConfirmationService], ngImport: i0, template: ` <div class="layout-topbar">
3595
+ <p-confirmDialog appendTo="body" />
3596
+ <div class="layout-topbar-logo-container">
3597
+ <button class="layout-menu-button layout-topbar-action" (click)="layoutService.onMenuToggle()">
3598
+ <i class="pi pi-bars"></i>
3599
+ </button>
3600
+ <a class="layout-topbar-logo" id="oip-app-topbar-logo-link" routerLink="">
3601
+ <ng-container
3602
+ *ngComponentOutlet="logoService.getLogoComponent(); inputs: { width: 36, height: 36 }"></ng-container>
3603
+ <span>{{ 'app-info.title' | translate }}</span>
3604
+ </a>
3605
+ </div>
2887
3606
 
2888
- @if (securityService.isAdmin() && topBarService.topBarItems.length > 0) {
2889
- <p-tabs class="layout-topbar-tabs ml-2" [(value)]="topBarService.activeId">
2890
- <p-tablist>
2891
- @for (tab of topBarService.availableTopBarItems; track tab.id) {
2892
- <p-tab id="oip-app-topbar-tab-{{ tab.id }}" [value]="tab.id">
2893
- <i class="pi {{ tab.icon }}"></i>
2894
- <span class="ml-2">{{ tab.caption }}</span>
2895
- </p-tab>
2896
- }
2897
- </p-tablist>
2898
- </p-tabs>
2899
- }
2900
- <div class="layout-topbar-actions">
2901
- <div class="layout-config-menu">
2902
- <app-topbar-application-switcher/>
2903
- <app-user-notifications/>
2904
- <p-button
2905
- class="layout-topbar-action"
2906
- id="oip-app-topbar-theme-button"
2907
- severity="secondary"
2908
- type="button"
2909
- [rounded]="true"
2910
- [text]="true"
2911
- (click)="toggleDarkMode()">
2912
- <i
2913
- class="pi"
2914
- [ngClass]="{
3607
+ @if (securityService.isAdmin() && topBarService.topBarItems.length > 0) {
3608
+ <p-tabs
3609
+ class="layout-topbar-tabs ml-2"
3610
+ [value]="topBarService.activeId"
3611
+ (valueChange)="onActiveTabChange($event)">
3612
+ <p-tablist>
3613
+ @for (tab of topBarService.availableTopBarItems; track tab.id) {
3614
+ <p-tab id="oip-app-topbar-tab-{{ tab.id }}" [value]="tab.id">
3615
+ <i class="pi {{ tab.icon }}"></i>
3616
+ <span class="ml-2">{{ tab.caption }}</span>
3617
+ </p-tab>
3618
+ }
3619
+ </p-tablist>
3620
+ </p-tabs>
3621
+ }
3622
+ <div class="layout-topbar-actions">
3623
+ <div class="layout-config-menu">
3624
+ <app-topbar-application-switcher />
3625
+ <app-user-notifications />
3626
+ <p-button
3627
+ class="layout-topbar-action"
3628
+ id="oip-app-topbar-theme-button"
3629
+ severity="secondary"
3630
+ type="button"
3631
+ [rounded]="true"
3632
+ [text]="true"
3633
+ (click)="toggleDarkMode()">
3634
+ <i
3635
+ class="pi"
3636
+ [ngClass]="{
2915
3637
  'pi-moon': layoutService.isDarkTheme(),
2916
3638
  'pi-sun': !layoutService.isDarkTheme()
2917
3639
  }"></i>
3640
+ </p-button>
3641
+ <div class="relative">
3642
+ <p-button
3643
+ class="layout-topbar-action layout-topbar-action-highlight"
3644
+ enterActiveClass="animate-scalein"
3645
+ enterFromClass="hidden"
3646
+ id="oip-app-topbar-palette-button"
3647
+ leaveActiveClass="animate-fadeout"
3648
+ leaveToClass="hidden"
3649
+ pStyleClass="@next"
3650
+ [hideOnOutsideClick]="true"
3651
+ [rounded]="true">
3652
+ <i class="pi pi-palette"></i>
2918
3653
  </p-button>
2919
- <div class="relative">
2920
- <p-button
2921
- class="layout-topbar-action layout-topbar-action-highlight"
2922
- enterActiveClass="animate-scalein"
2923
- enterFromClass="hidden"
2924
- id="oip-app-topbar-palette-button"
2925
- leaveActiveClass="animate-fadeout"
2926
- leaveToClass="hidden"
2927
- pStyleClass="@next"
2928
- [hideOnOutsideClick]="true"
2929
- [rounded]="true">
2930
- <i class="pi pi-palette"></i>
2931
- </p-button>
2932
- <app-configurator/>
2933
- </div>
3654
+ <app-configurator />
2934
3655
  </div>
3656
+ </div>
2935
3657
 
2936
- <button
2937
- class="layout-topbar-menu-button layout-topbar-action"
2938
- enterActiveClass="animate-scalein"
2939
- enterFromClass="hidden"
2940
- id="oip-app-topbar-menu-expand-button"
2941
- leaveActiveClass="animate-fadeout"
2942
- leaveToClass="hidden"
2943
- pStyleClass="@next"
2944
- [hideOnOutsideClick]="true">
2945
- <i class="pi pi-ellipsis-v"></i>
2946
- </button>
3658
+ <button
3659
+ class="layout-topbar-menu-button layout-topbar-action"
3660
+ enterActiveClass="animate-scalein"
3661
+ enterFromClass="hidden"
3662
+ id="oip-app-topbar-menu-expand-button"
3663
+ leaveActiveClass="animate-fadeout"
3664
+ leaveToClass="hidden"
3665
+ pStyleClass="@next"
3666
+ [hideOnOutsideClick]="true">
3667
+ <i class="pi pi-ellipsis-v"></i>
3668
+ </button>
2947
3669
 
2948
- <div class="layout-topbar-menu hidden lg:block">
2949
- <div class="layout-topbar-menu-content">
2950
- <button
2951
- class="layout-topbar-action"
2952
- id="oip-app-topbar-logout-button"
2953
- type="button"
2954
- (click)="confirmLogout()"
2955
- (keydown)="logoutKeyDown($event)">
2956
- <i class="pi pi-sign-out"></i>
2957
- <span>{{ 'topbar.logout' | translate }}</span>
2958
- </button>
2959
- <button class="layout-topbar-action" routerLink="config">
2960
- <p-avatar
2961
- class="p-link flex align-items-center"
2962
- id="oip-app-topbar-user-avatar"
2963
- shape="circle"
2964
- size="normal"
2965
- [image]="userService.photoLoaded ? userService.photo : null"
3670
+ <div class="layout-topbar-menu hidden lg:block">
3671
+ <div class="layout-topbar-menu-content">
3672
+ <button
3673
+ class="layout-topbar-action"
3674
+ id="oip-app-topbar-logout-button"
3675
+ type="button"
3676
+ (click)="confirmLogout()"
3677
+ (keydown)="logoutKeyDown($event)">
3678
+ <i class="pi pi-sign-out"></i>
3679
+ <span>{{ 'topbar.logout' | translate }}</span>
3680
+ </button>
3681
+ <button class="layout-topbar-action" routerLink="config">
3682
+ <p-avatar
3683
+ class="p-link flex align-items-center"
3684
+ id="oip-app-topbar-user-avatar"
3685
+ shape="circle"
3686
+ size="normal"
3687
+ [image]="userService.photoLoaded ? userService.photo : null"
2966
3688
  >{{ !userService.photoLoaded ? userService.shortLabel : null }}
2967
- </p-avatar>
2968
- <span class="ml-2">{{ 'topbar.profile' | translate }}</span>
2969
- </button>
2970
- </div>
3689
+ </p-avatar>
3690
+ <span class="ml-2">{{ 'topbar.profile' | translate }}</span>
3691
+ </button>
2971
3692
  </div>
2972
3693
  </div>
2973
- </div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$5.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$3.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i3$3.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "component", type: AppConfiguratorComponent, selector: "app-configurator" }, { kind: "component", type: Tabs, selector: "p-tabs", inputs: ["value", "scrollable", "lazy", "selectOnFocus", "showNavigators", "tabindex"], outputs: ["valueChange"] }, { kind: "component", type: TabList, selector: "p-tablist" }, { kind: "component", type: Tab, selector: "p-tab", inputs: ["value", "disabled"], outputs: ["valueChange"] }, { kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i2$1.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { 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: "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: UserNotificationsComponent, selector: "app-user-notifications" }, { kind: "component", type: AppTopbarApplicationSwitcherComponent, selector: "app-topbar-application-switcher" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
3694
+ </div>
3695
+ </div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$5.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$3.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i3$3.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "component", type: AppConfiguratorComponent, selector: "app-configurator" }, { kind: "component", type: Tabs, selector: "p-tabs", inputs: ["value", "scrollable", "lazy", "selectOnFocus", "showNavigators", "tabindex"], outputs: ["valueChange"] }, { kind: "component", type: TabList, selector: "p-tablist" }, { kind: "component", type: Tab, selector: "p-tab", inputs: ["value", "disabled"], outputs: ["valueChange"] }, { kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i2$1.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { 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: "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: UserNotificationsComponent, selector: "app-user-notifications" }, { kind: "component", type: AppTopbarApplicationSwitcherComponent, selector: "app-topbar-application-switcher" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
2974
3696
  }
2975
3697
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: AppTopbar, decorators: [{
2976
3698
  type: Component,
@@ -2992,106 +3714,108 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
2992
3714
  TranslatePipe,
2993
3715
  AppTopbarApplicationSwitcherComponent
2994
3716
  ],
2995
- template: `
2996
- <div class="layout-topbar">
2997
- <p-confirmDialog appendTo="body"/>
2998
- <div class="layout-topbar-logo-container">
2999
- <button class="layout-menu-button layout-topbar-action" (click)="layoutService.onMenuToggle()">
3000
- <i class="pi pi-bars"></i>
3001
- </button>
3002
- <a class="layout-topbar-logo" id="oip-app-topbar-logo-link" routerLink="">
3003
- <ng-container
3004
- *ngComponentOutlet="logoService.getLogoComponent(); inputs: { width: 36, height: 36 }"></ng-container>
3005
- <span>{{ 'app-info.title' | translate }}</span>
3006
- </a>
3007
- </div>
3717
+ template: ` <div class="layout-topbar">
3718
+ <p-confirmDialog appendTo="body" />
3719
+ <div class="layout-topbar-logo-container">
3720
+ <button class="layout-menu-button layout-topbar-action" (click)="layoutService.onMenuToggle()">
3721
+ <i class="pi pi-bars"></i>
3722
+ </button>
3723
+ <a class="layout-topbar-logo" id="oip-app-topbar-logo-link" routerLink="">
3724
+ <ng-container
3725
+ *ngComponentOutlet="logoService.getLogoComponent(); inputs: { width: 36, height: 36 }"></ng-container>
3726
+ <span>{{ 'app-info.title' | translate }}</span>
3727
+ </a>
3728
+ </div>
3008
3729
 
3009
- @if (securityService.isAdmin() && topBarService.topBarItems.length > 0) {
3010
- <p-tabs class="layout-topbar-tabs ml-2" [(value)]="topBarService.activeId">
3011
- <p-tablist>
3012
- @for (tab of topBarService.availableTopBarItems; track tab.id) {
3013
- <p-tab id="oip-app-topbar-tab-{{ tab.id }}" [value]="tab.id">
3014
- <i class="pi {{ tab.icon }}"></i>
3015
- <span class="ml-2">{{ tab.caption }}</span>
3016
- </p-tab>
3017
- }
3018
- </p-tablist>
3019
- </p-tabs>
3020
- }
3021
- <div class="layout-topbar-actions">
3022
- <div class="layout-config-menu">
3023
- <app-topbar-application-switcher/>
3024
- <app-user-notifications/>
3025
- <p-button
3026
- class="layout-topbar-action"
3027
- id="oip-app-topbar-theme-button"
3028
- severity="secondary"
3029
- type="button"
3030
- [rounded]="true"
3031
- [text]="true"
3032
- (click)="toggleDarkMode()">
3033
- <i
3034
- class="pi"
3035
- [ngClass]="{
3730
+ @if (securityService.isAdmin() && topBarService.topBarItems.length > 0) {
3731
+ <p-tabs
3732
+ class="layout-topbar-tabs ml-2"
3733
+ [value]="topBarService.activeId"
3734
+ (valueChange)="onActiveTabChange($event)">
3735
+ <p-tablist>
3736
+ @for (tab of topBarService.availableTopBarItems; track tab.id) {
3737
+ <p-tab id="oip-app-topbar-tab-{{ tab.id }}" [value]="tab.id">
3738
+ <i class="pi {{ tab.icon }}"></i>
3739
+ <span class="ml-2">{{ tab.caption }}</span>
3740
+ </p-tab>
3741
+ }
3742
+ </p-tablist>
3743
+ </p-tabs>
3744
+ }
3745
+ <div class="layout-topbar-actions">
3746
+ <div class="layout-config-menu">
3747
+ <app-topbar-application-switcher />
3748
+ <app-user-notifications />
3749
+ <p-button
3750
+ class="layout-topbar-action"
3751
+ id="oip-app-topbar-theme-button"
3752
+ severity="secondary"
3753
+ type="button"
3754
+ [rounded]="true"
3755
+ [text]="true"
3756
+ (click)="toggleDarkMode()">
3757
+ <i
3758
+ class="pi"
3759
+ [ngClass]="{
3036
3760
  'pi-moon': layoutService.isDarkTheme(),
3037
3761
  'pi-sun': !layoutService.isDarkTheme()
3038
3762
  }"></i>
3763
+ </p-button>
3764
+ <div class="relative">
3765
+ <p-button
3766
+ class="layout-topbar-action layout-topbar-action-highlight"
3767
+ enterActiveClass="animate-scalein"
3768
+ enterFromClass="hidden"
3769
+ id="oip-app-topbar-palette-button"
3770
+ leaveActiveClass="animate-fadeout"
3771
+ leaveToClass="hidden"
3772
+ pStyleClass="@next"
3773
+ [hideOnOutsideClick]="true"
3774
+ [rounded]="true">
3775
+ <i class="pi pi-palette"></i>
3039
3776
  </p-button>
3040
- <div class="relative">
3041
- <p-button
3042
- class="layout-topbar-action layout-topbar-action-highlight"
3043
- enterActiveClass="animate-scalein"
3044
- enterFromClass="hidden"
3045
- id="oip-app-topbar-palette-button"
3046
- leaveActiveClass="animate-fadeout"
3047
- leaveToClass="hidden"
3048
- pStyleClass="@next"
3049
- [hideOnOutsideClick]="true"
3050
- [rounded]="true">
3051
- <i class="pi pi-palette"></i>
3052
- </p-button>
3053
- <app-configurator/>
3054
- </div>
3777
+ <app-configurator />
3055
3778
  </div>
3779
+ </div>
3056
3780
 
3057
- <button
3058
- class="layout-topbar-menu-button layout-topbar-action"
3059
- enterActiveClass="animate-scalein"
3060
- enterFromClass="hidden"
3061
- id="oip-app-topbar-menu-expand-button"
3062
- leaveActiveClass="animate-fadeout"
3063
- leaveToClass="hidden"
3064
- pStyleClass="@next"
3065
- [hideOnOutsideClick]="true">
3066
- <i class="pi pi-ellipsis-v"></i>
3067
- </button>
3781
+ <button
3782
+ class="layout-topbar-menu-button layout-topbar-action"
3783
+ enterActiveClass="animate-scalein"
3784
+ enterFromClass="hidden"
3785
+ id="oip-app-topbar-menu-expand-button"
3786
+ leaveActiveClass="animate-fadeout"
3787
+ leaveToClass="hidden"
3788
+ pStyleClass="@next"
3789
+ [hideOnOutsideClick]="true">
3790
+ <i class="pi pi-ellipsis-v"></i>
3791
+ </button>
3068
3792
 
3069
- <div class="layout-topbar-menu hidden lg:block">
3070
- <div class="layout-topbar-menu-content">
3071
- <button
3072
- class="layout-topbar-action"
3073
- id="oip-app-topbar-logout-button"
3074
- type="button"
3075
- (click)="confirmLogout()"
3076
- (keydown)="logoutKeyDown($event)">
3077
- <i class="pi pi-sign-out"></i>
3078
- <span>{{ 'topbar.logout' | translate }}</span>
3079
- </button>
3080
- <button class="layout-topbar-action" routerLink="config">
3081
- <p-avatar
3082
- class="p-link flex align-items-center"
3083
- id="oip-app-topbar-user-avatar"
3084
- shape="circle"
3085
- size="normal"
3086
- [image]="userService.photoLoaded ? userService.photo : null"
3793
+ <div class="layout-topbar-menu hidden lg:block">
3794
+ <div class="layout-topbar-menu-content">
3795
+ <button
3796
+ class="layout-topbar-action"
3797
+ id="oip-app-topbar-logout-button"
3798
+ type="button"
3799
+ (click)="confirmLogout()"
3800
+ (keydown)="logoutKeyDown($event)">
3801
+ <i class="pi pi-sign-out"></i>
3802
+ <span>{{ 'topbar.logout' | translate }}</span>
3803
+ </button>
3804
+ <button class="layout-topbar-action" routerLink="config">
3805
+ <p-avatar
3806
+ class="p-link flex align-items-center"
3807
+ id="oip-app-topbar-user-avatar"
3808
+ shape="circle"
3809
+ size="normal"
3810
+ [image]="userService.photoLoaded ? userService.photo : null"
3087
3811
  >{{ !userService.photoLoaded ? userService.shortLabel : null }}
3088
- </p-avatar>
3089
- <span class="ml-2">{{ 'topbar.profile' | translate }}</span>
3090
- </button>
3091
- </div>
3812
+ </p-avatar>
3813
+ <span class="ml-2">{{ 'topbar.profile' | translate }}</span>
3814
+ </button>
3092
3815
  </div>
3093
3816
  </div>
3094
- </div>`,
3817
+ </div>
3818
+ </div>`,
3095
3819
  providers: [ConfirmationService]
3096
3820
  }]
3097
3821
  }] });
@@ -3148,6 +3872,14 @@ class MenuApi extends HttpClient {
3148
3872
  format: "json",
3149
3873
  ...params,
3150
3874
  });
3875
+ this.getModuleInstanceRights = (query, params = {}) => this.request({
3876
+ path: `/api/menu/get-module-instance-rights`,
3877
+ method: "GET",
3878
+ query: query,
3879
+ secure: true,
3880
+ format: "json",
3881
+ ...params,
3882
+ });
3151
3883
  this.getAdminMenu = (params = {}) => this.request({
3152
3884
  path: `/api/menu/get-admin-menu`,
3153
3885
  method: "GET",
@@ -3206,10 +3938,16 @@ class MenuService {
3206
3938
  this.resetSource = new Subject();
3207
3939
  this.titleService = inject(AppTitleService);
3208
3940
  this.menuDataService = inject(MenuApi);
3941
+ this.layoutService = inject(LayoutService);
3209
3942
  this.menuSource$ = this.menuSource.asObservable();
3210
3943
  this.resetSource$ = this.resetSource.asObservable();
3211
3944
  this.menu = [];
3212
- this.adminMode = false;
3945
+ }
3946
+ get adminMode() {
3947
+ return this.layoutService.adminMode();
3948
+ }
3949
+ set adminMode(value) {
3950
+ this.layoutService.layoutConfig.update((config) => ({ ...config, adminMode: value }));
3213
3951
  }
3214
3952
  async loadMenu() {
3215
3953
  this.menu = this.adminMode ? await this.getAdminMenu() : await this.getMenu();
@@ -3560,7 +4298,7 @@ class MenuItemComponent {
3560
4298
  </ul>
3561
4299
  }
3562
4300
  </ng-container>
3563
- `, isInline: true, dependencies: [{ kind: "component", type: MenuItemComponent, selector: "[app-menuitem]", inputs: ["item", "index", "root", "parentKey", "menuItemCreateDialogComponent", "menuItemEditDialogComponent", "contextMenu"] }, { kind: "ngmodule", type: RippleModule }, { kind: "directive", type: i3$4.Ripple, selector: "[pRipple]" }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: ContextMenuModule }, { 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"] }] }); }
4301
+ `, isInline: true, dependencies: [{ kind: "component", type: MenuItemComponent, selector: "[app-menuitem]", inputs: ["item", "index", "root", "parentKey", "menuItemCreateDialogComponent", "menuItemEditDialogComponent", "contextMenu"] }, { kind: "ngmodule", type: RippleModule }, { kind: "directive", type: i2$2.Ripple, selector: "[pRipple]" }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: ContextMenuModule }, { 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"] }] }); }
3564
4302
  }
3565
4303
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: MenuItemComponent, decorators: [{
3566
4304
  type: Component,
@@ -3762,10 +4500,14 @@ class MenuItemCreateDialogComponent {
3762
4500
  <p-select
3763
4501
  appendTo="body"
3764
4502
  class="flex-auto"
4503
+ filterBy="value"
4504
+ filterPlaceholder="{{ 'menuItemCreateDialogComponent.searchModule' | translate }}"
3765
4505
  id="oip-menu-item-create-module"
3766
4506
  optionLabel="value"
3767
4507
  optionValue="key"
3768
4508
  placeholder="{{ 'menuItemCreateDialogComponent.selectModule' | translate }}"
4509
+ scrollHeight="18rem"
4510
+ [filter]="true"
3769
4511
  [options]="modules"
3770
4512
  [(ngModel)]="selectModule" />
3771
4513
  </div>
@@ -3813,7 +4555,7 @@ class MenuItemCreateDialogComponent {
3813
4555
  (click)="save()" />
3814
4556
  </div>
3815
4557
  </p-dialog>
3816
- `, 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$2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i3$5.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$1.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" }] }); }
4558
+ `, 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$4.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$1.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" }] }); }
3817
4559
  }
3818
4560
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: MenuItemCreateDialogComponent, decorators: [{
3819
4561
  type: Component,
@@ -3855,10 +4597,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
3855
4597
  <p-select
3856
4598
  appendTo="body"
3857
4599
  class="flex-auto"
4600
+ filterBy="value"
4601
+ filterPlaceholder="{{ 'menuItemCreateDialogComponent.searchModule' | translate }}"
3858
4602
  id="oip-menu-item-create-module"
3859
4603
  optionLabel="value"
3860
4604
  optionValue="key"
3861
4605
  placeholder="{{ 'menuItemCreateDialogComponent.selectModule' | translate }}"
4606
+ scrollHeight="18rem"
4607
+ [filter]="true"
3862
4608
  [options]="modules"
3863
4609
  [(ngModel)]="selectModule" />
3864
4610
  </div>
@@ -3920,7 +4666,6 @@ class MenuItemEditDialogComponent {
3920
4666
  this.securityApi = inject(SecurityApi);
3921
4667
  this.msgService = inject(MsgService);
3922
4668
  this.visibleChange = new EventEmitter();
3923
- this.modules = [];
3924
4669
  this.roles = [];
3925
4670
  this.iconOptions = Object.values(PrimeIcons)
3926
4671
  .filter((icon) => typeof icon === 'string')
@@ -3974,9 +4719,6 @@ class MenuItemEditDialogComponent {
3974
4719
  viewRoles: this.menuService.contextMenuItem?.securities
3975
4720
  };
3976
4721
  this.roles = await this.securityApi.getRealmRoles();
3977
- this.menuService.getModules().then((data) => {
3978
- this.modules = data;
3979
- });
3980
4722
  this.visible = true;
3981
4723
  this.visibleChange.emit(this.visible);
3982
4724
  }
@@ -4059,7 +4801,7 @@ class MenuItemEditDialogComponent {
4059
4801
  (click)="save()" />
4060
4802
  </div>
4061
4803
  </p-dialog>
4062
- `, 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$2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i3$5.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$1.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" }] }); }
4804
+ `, 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$4.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$1.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" }] }); }
4063
4805
  }
4064
4806
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: MenuItemEditDialogComponent, decorators: [{
4065
4807
  type: Component,
@@ -4179,8 +4921,7 @@ class MenuComponent {
4179
4921
  ];
4180
4922
  }
4181
4923
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: MenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4182
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.24", type: MenuComponent, isStandalone: true, selector: "app-menu", providers: [MenuApi], viewQueries: [{ propertyName: "menuItemCreateDialogComponent", first: true, predicate: MenuItemCreateDialogComponent, descendants: true }, { propertyName: "menuItemEditDialogComponent", first: true, predicate: MenuItemEditDialogComponent, descendants: true }, { propertyName: "contextMenu", first: true, predicate: ContextMenu, descendants: true }], ngImport: i0, template: `
4183
- <div #empty class="layout-sidebar" (contextmenu)="onContextMenu($event)">
4924
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.24", type: MenuComponent, isStandalone: true, selector: "app-menu", providers: [MenuApi], viewQueries: [{ propertyName: "menuItemCreateDialogComponent", first: true, predicate: MenuItemCreateDialogComponent, descendants: true }, { propertyName: "menuItemEditDialogComponent", first: true, predicate: MenuItemEditDialogComponent, descendants: true }, { propertyName: "contextMenu", first: true, predicate: ContextMenu, descendants: true }], ngImport: i0, template: ` <div #empty class="layout-sidebar" (contextmenu)="onContextMenu($event)">
4184
4925
  <ul class="layout-menu">
4185
4926
  @for (item of menuService.menu; track item; let i = $index) {
4186
4927
  <ng-container>
@@ -4201,9 +4942,9 @@ class MenuComponent {
4201
4942
  </ul>
4202
4943
  </div>
4203
4944
  @if (securityService.isAdmin()) {
4204
- <p-contextMenu [target]="empty"/>
4205
- <menu-item-create-dialog/>
4206
- <menu-item-edit-dialog/>
4945
+ <p-contextMenu [target]="empty" />
4946
+ <menu-item-create-dialog />
4947
+ <menu-item-edit-dialog />
4207
4948
  }`, isInline: true, dependencies: [{ kind: "component", type: MenuItemComponent, selector: "[app-menuitem]", inputs: ["item", "index", "root", "parentKey", "menuItemCreateDialogComponent", "menuItemEditDialogComponent", "contextMenu"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: ContextMenuModule }, { kind: "component", type: i1$6.ContextMenu, selector: "p-contextMenu, p-contextmenu, p-context-menu", inputs: ["model", "triggerEvent", "target", "global", "style", "styleClass", "autoZIndex", "baseZIndex", "id", "breakpoint", "ariaLabel", "ariaLabelledBy", "pressDelay", "appendTo"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: DialogModule }, { kind: "ngmodule", type: InputTextModule }, { kind: "component", type: MenuItemCreateDialogComponent, selector: "menu-item-create-dialog", inputs: ["visible"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: MenuItemEditDialogComponent, selector: "menu-item-edit-dialog", inputs: ["visible"], outputs: ["visibleChange"] }] }); }
4208
4949
  }
4209
4950
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: MenuComponent, decorators: [{
@@ -4222,8 +4963,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
4222
4963
  providers: [MenuApi],
4223
4964
  selector: 'app-menu',
4224
4965
  standalone: true,
4225
- template: `
4226
- <div #empty class="layout-sidebar" (contextmenu)="onContextMenu($event)">
4966
+ template: ` <div #empty class="layout-sidebar" (contextmenu)="onContextMenu($event)">
4227
4967
  <ul class="layout-menu">
4228
4968
  @for (item of menuService.menu; track item; let i = $index) {
4229
4969
  <ng-container>
@@ -4244,9 +4984,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
4244
4984
  </ul>
4245
4985
  </div>
4246
4986
  @if (securityService.isAdmin()) {
4247
- <p-contextMenu [target]="empty"/>
4248
- <menu-item-create-dialog/>
4249
- <menu-item-edit-dialog/>
4987
+ <p-contextMenu [target]="empty" />
4988
+ <menu-item-create-dialog />
4989
+ <menu-item-edit-dialog />
4250
4990
  }`
4251
4991
  }]
4252
4992
  }], propDecorators: { menuItemCreateDialogComponent: [{
@@ -4467,6 +5207,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
4467
5207
  }]
4468
5208
  }] });
4469
5209
 
5210
+ var notfound$1 = {
5211
+ title: "Not Found",
5212
+ errorCode: "404",
5213
+ description: "Requested resource is not available.",
5214
+ button: "Go to home"
5215
+ };
5216
+ var en$5 = {
5217
+ notfound: notfound$1
5218
+ };
5219
+
5220
+ var notfound = {
5221
+ title: "Не найдено",
5222
+ errorCode: "404",
5223
+ description: "Запрашиваемый ресурс недоступен.",
5224
+ button: "На главную"
5225
+ };
5226
+ var ru$5 = {
5227
+ notfound: notfound
5228
+ };
5229
+
5230
+ L10nService.registerTranslations({ en: en$5, ru: ru$5 });
4470
5231
  class NotfoundComponent {
4471
5232
  constructor(l10nService) {
4472
5233
  l10nService.get('notfound');
@@ -4492,7 +5253,7 @@ class NotfoundComponent {
4492
5253
  </div>
4493
5254
  </div>
4494
5255
  </div>
4495
- </div>`, isInline: true, dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: LogoComponent, selector: "logo", inputs: ["width", "height"] }, { kind: "component", type: 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: "component", type: AppFloatingConfiguratorComponent, selector: "app-floating-configurator" }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i2$3.TranslatePipe, name: "translate" }] }); }
5256
+ </div>`, isInline: true, dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: LogoComponent, selector: "logo", inputs: ["width", "height"] }, { kind: "component", type: 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: "component", type: AppFloatingConfiguratorComponent, selector: "app-floating-configurator" }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i2$4.TranslatePipe, name: "translate" }] }); }
4496
5257
  }
4497
5258
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: NotfoundComponent, decorators: [{
4498
5259
  type: Component,
@@ -4528,14 +5289,18 @@ class UnauthorizedComponent {
4528
5289
  constructor() {
4529
5290
  this.securityService = inject(SecurityService);
4530
5291
  this.route = inject(ActivatedRoute);
4531
- this.router = inject(Router);
4532
5292
  }
4533
- signIn() {
4534
- const returnUrl = this.route.snapshot.queryParamMap.get('returnUrl') || '/';
4535
- this.router.navigate(['/unauthorized'], {
4536
- queryParams: { returnUrl },
4537
- replaceUrl: true
4538
- }).then(() => this.securityService.authorize());
5293
+ ngOnInit() {
5294
+ const returnUrl = sessionStorage.getItem(BffSecurityService.authorizeAfterLogoutReturnUrlKey);
5295
+ if (!returnUrl) {
5296
+ return;
5297
+ }
5298
+ sessionStorage.removeItem(BffSecurityService.authorizeAfterLogoutReturnUrlKey);
5299
+ this.securityService.authorize(returnUrl);
5300
+ }
5301
+ signIn() {
5302
+ const returnUrl = this.route.snapshot.queryParamMap.get('returnUrl') || '/';
5303
+ this.securityService.authorize(returnUrl);
4539
5304
  }
4540
5305
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: UnauthorizedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4541
5306
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.24", type: UnauthorizedComponent, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
@@ -4611,6 +5376,80 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
4611
5376
  }]
4612
5377
  }] });
4613
5378
 
5379
+ class AccessComponent {
5380
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: AccessComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5381
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.24", type: AccessComponent, isStandalone: true, selector: "app-access", ngImport: i0, template: `<div
5382
+ class="surface-ground flex align-items-center justify-content-center min-h-screen min-w-screen overflow-hidden">
5383
+ <div class="flex flex-column align-items-center justify-content-center">
5384
+ <img alt="Sakai logo" class="mb-5 w-6rem flex-shrink-0" src="assets/demo/images/access/logo-orange.svg" />
5385
+ <div
5386
+ style="border-radius:56px; padding:0.3rem; background: linear-gradient(180deg, rgba(247, 149, 48, 0.4) 10%, rgba(247, 149, 48, 0) 30%);">
5387
+ <div
5388
+ class="w-full surface-card py-8 px-5 sm:px-8 flex flex-column align-items-center"
5389
+ style="border-radius:53px">
5390
+ <div class="grid flex-column align-items-center">
5391
+ <div
5392
+ class="flex justify-content-center align-items-center bg-orange-500 border-circle"
5393
+ style="width:3.2rem; height:3.2rem;">
5394
+ <i class="text-50 pi pi-fw pi-lock text-2xl"></i>
5395
+ </div>
5396
+ <h1 class="text-900 font-bold text-4xl lg:text-5xl mb-2">Access Denied</h1>
5397
+ <span class="text-600 mb-5">You do not have the necessary permisions. Please contact admins.</span>
5398
+ <img alt="Access denied" class="mb-5" src="assets/demo/images/access/asset-access.svg" width="80%" />
5399
+ <p-button
5400
+ class="p-button-text"
5401
+ icon="pi pi-arrow-left"
5402
+ id="oip-app-access-go-to-dashboard-button"
5403
+ label="Go to Dashboard"
5404
+ pButton
5405
+ pRipple
5406
+ [routerLink]="['/']"></p-button>
5407
+ </div>
5408
+ </div>
5409
+ </div>
5410
+ </div>
5411
+ </div>`, isInline: true, dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i1$2.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { 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: RippleModule }, { kind: "directive", type: i2$2.Ripple, selector: "[pRipple]" }] }); }
5412
+ }
5413
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: AccessComponent, decorators: [{
5414
+ type: Component,
5415
+ args: [{
5416
+ selector: 'app-access',
5417
+ template: `<div
5418
+ class="surface-ground flex align-items-center justify-content-center min-h-screen min-w-screen overflow-hidden">
5419
+ <div class="flex flex-column align-items-center justify-content-center">
5420
+ <img alt="Sakai logo" class="mb-5 w-6rem flex-shrink-0" src="assets/demo/images/access/logo-orange.svg" />
5421
+ <div
5422
+ style="border-radius:56px; padding:0.3rem; background: linear-gradient(180deg, rgba(247, 149, 48, 0.4) 10%, rgba(247, 149, 48, 0) 30%);">
5423
+ <div
5424
+ class="w-full surface-card py-8 px-5 sm:px-8 flex flex-column align-items-center"
5425
+ style="border-radius:53px">
5426
+ <div class="grid flex-column align-items-center">
5427
+ <div
5428
+ class="flex justify-content-center align-items-center bg-orange-500 border-circle"
5429
+ style="width:3.2rem; height:3.2rem;">
5430
+ <i class="text-50 pi pi-fw pi-lock text-2xl"></i>
5431
+ </div>
5432
+ <h1 class="text-900 font-bold text-4xl lg:text-5xl mb-2">Access Denied</h1>
5433
+ <span class="text-600 mb-5">You do not have the necessary permisions. Please contact admins.</span>
5434
+ <img alt="Access denied" class="mb-5" src="assets/demo/images/access/asset-access.svg" width="80%" />
5435
+ <p-button
5436
+ class="p-button-text"
5437
+ icon="pi pi-arrow-left"
5438
+ id="oip-app-access-go-to-dashboard-button"
5439
+ label="Go to Dashboard"
5440
+ pButton
5441
+ pRipple
5442
+ [routerLink]="['/']"></p-button>
5443
+ </div>
5444
+ </div>
5445
+ </div>
5446
+ </div>
5447
+ </div>`,
5448
+ imports: [RouterLink, ButtonModule, RippleModule],
5449
+ standalone: true
5450
+ }]
5451
+ }] });
5452
+
4614
5453
  class ErrorComponent {
4615
5454
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ErrorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4616
5455
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.24", type: ErrorComponent, isStandalone: true, selector: "app-error", ngImport: i0, template: `<div
@@ -4643,7 +5482,7 @@ class ErrorComponent {
4643
5482
  </div>
4644
5483
  </div>
4645
5484
  </div>
4646
- </div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i1$2.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "ngmodule", type: RippleModule }, { kind: "directive", type: i3$4.Ripple, selector: "[pRipple]" }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
5485
+ </div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i1$2.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "ngmodule", type: RippleModule }, { kind: "directive", type: i2$2.Ripple, selector: "[pRipple]" }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
4647
5486
  }
4648
5487
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ErrorComponent, decorators: [{
4649
5488
  type: Component,
@@ -4690,9 +5529,21 @@ class ProfileComponent {
4690
5529
  this.userService = inject(UserService);
4691
5530
  this.msgService = inject(MsgService);
4692
5531
  this.translateService = inject(TranslateService);
5532
+ this.userProfileApi = inject(UserProfileApi);
4693
5533
  }
4694
- onBasicUploadAuto($event) {
4695
- this.msgService.success(this.translateService.instant('profileComponent.successfullyUploaded'));
5534
+ async uploadPhoto(event) {
5535
+ const file = event.files?.[0];
5536
+ if (!file) {
5537
+ return;
5538
+ }
5539
+ try {
5540
+ await this.userProfileApi.postUserPhoto({ files: file });
5541
+ this.userService.refreshUserPhoto();
5542
+ this.msgService.success(this.translateService.instant('profileComponent.successfullyUploaded'));
5543
+ }
5544
+ catch (error) {
5545
+ this.msgService.error(error);
5546
+ }
4696
5547
  }
4697
5548
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ProfileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4698
5549
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.24", type: ProfileComponent, isStandalone: true, selector: "user-profile", ngImport: i0, template: `
@@ -4711,10 +5562,9 @@ class ProfileComponent {
4711
5562
  maxFileSize="1000000"
4712
5563
  mode="basic"
4713
5564
  name="files"
4714
- url="/api/user-profile/post-user-photo"
4715
- withCredentials="true"
4716
5565
  [auto]="true"
4717
- (onUpload)="onBasicUploadAuto($event)" />
5566
+ [customUpload]="true"
5567
+ (uploadHandler)="uploadPhoto($event)" />
4718
5568
  </div>
4719
5569
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: FileUploadModule }, { kind: "component", type: i1$7.FileUpload, selector: "p-fileupload, p-fileUpload", inputs: ["name", "url", "method", "multiple", "accept", "disabled", "auto", "withCredentials", "maxFileSize", "invalidFileSizeMessageSummary", "invalidFileSizeMessageDetail", "invalidFileTypeMessageSummary", "invalidFileTypeMessageDetail", "invalidFileLimitMessageDetail", "invalidFileLimitMessageSummary", "style", "styleClass", "previewWidth", "chooseLabel", "uploadLabel", "cancelLabel", "chooseIcon", "uploadIcon", "cancelIcon", "showUploadButton", "showCancelButton", "mode", "headers", "customUpload", "fileLimit", "uploadStyleClass", "cancelStyleClass", "removeStyleClass", "chooseStyleClass", "chooseButtonProps", "uploadButtonProps", "cancelButtonProps", "files"], outputs: ["onBeforeUpload", "onSend", "onUpload", "onError", "onClear", "onRemove", "onSelect", "onProgress", "uploadHandler", "onImageError", "onRemoveUploadedFile"] }, { kind: "ngmodule", type: ImageModule }, { kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i2$1.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
4720
5570
  }
@@ -4723,7 +5573,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
4723
5573
  args: [{
4724
5574
  selector: 'user-profile',
4725
5575
  standalone: true,
4726
- imports: [FileUploadModule, ImageModule, ImageModule, FileUploadModule, ImageModule, AvatarModule, TranslatePipe],
5576
+ imports: [FileUploadModule, ImageModule, AvatarModule, TranslatePipe],
4727
5577
  template: `
4728
5578
  <p-avatar
4729
5579
  class="mr-2"
@@ -4740,15 +5590,55 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
4740
5590
  maxFileSize="1000000"
4741
5591
  mode="basic"
4742
5592
  name="files"
4743
- url="/api/user-profile/post-user-photo"
4744
- withCredentials="true"
4745
5593
  [auto]="true"
4746
- (onUpload)="onBasicUploadAuto($event)" />
5594
+ [customUpload]="true"
5595
+ (uploadHandler)="uploadPhoto($event)" />
4747
5596
  </div>
4748
5597
  `
4749
5598
  }]
4750
5599
  }] });
4751
5600
 
5601
+ var config$1 = {
5602
+ all: "All",
5603
+ applicationManagement: "Application management",
5604
+ dateFormat: "Date format",
5605
+ dateTimeFormat: "Date and time format:",
5606
+ goTo: "Go to",
5607
+ localization: "Localization",
5608
+ menu: "Menu",
5609
+ moduleManagement: "Module management",
5610
+ photo: "Photo",
5611
+ profile: "Profile",
5612
+ selectLanguage: "Select language",
5613
+ timeFormat: "Time format",
5614
+ timeZone: "Time zone",
5615
+ usePhoto256x256Pixel: "Use photo 256x256 pixel"
5616
+ };
5617
+ var en$4 = {
5618
+ config: config$1
5619
+ };
5620
+
5621
+ var config = {
5622
+ all: "Все",
5623
+ applicationManagement: "Управление приложениями",
5624
+ dateFormat: "Формат даты",
5625
+ dateTimeFormat: "Формат даты и времени:",
5626
+ goTo: "Перейти",
5627
+ localization: "Локализация",
5628
+ menu: "Меню",
5629
+ moduleManagement: "Управление модулями",
5630
+ photo: "Фото",
5631
+ profile: "Профиль",
5632
+ selectLanguage: "Выберите язык",
5633
+ timeFormat: "Формат времени",
5634
+ timeZone: "Часовой пояс",
5635
+ usePhoto256x256Pixel: "Используйте фото 256x256 пикселей"
5636
+ };
5637
+ var ru$4 = {
5638
+ config: config
5639
+ };
5640
+
5641
+ L10nService.registerTranslations({ en: en$4, ru: ru$4 });
4752
5642
  class ConfigComponent {
4753
5643
  constructor() {
4754
5644
  this.layoutService = inject(LayoutService);
@@ -4890,7 +5780,7 @@ class ConfigComponent {
4890
5780
  </div>
4891
5781
  }
4892
5782
  </div>
4893
- `, isInline: true, dependencies: [{ kind: "component", type: ProfileComponent, selector: "user-profile" }, { 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: "ngmodule", type: FormsModule }, { 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: "component", type: 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: TableModule }, { kind: "ngmodule", type: ToggleSwitchModule }, { kind: "component", type: i2$4.ToggleSwitch, selector: "p-toggleswitch, p-toggleSwitch, p-toggle-switch", inputs: ["styleClass", "tabindex", "inputId", "readonly", "trueValue", "falseValue", "ariaLabel", "size", "ariaLabelledBy", "autofocus"], outputs: ["onChange"] }, { kind: "component", type: 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: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
5783
+ `, isInline: true, dependencies: [{ kind: "component", type: ProfileComponent, selector: "user-profile" }, { 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: "ngmodule", type: FormsModule }, { 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: "component", type: 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: TableModule }, { kind: "ngmodule", type: ToggleSwitchModule }, { kind: "component", type: i2$5.ToggleSwitch, selector: "p-toggleswitch, p-toggleSwitch, p-toggle-switch", inputs: ["styleClass", "tabindex", "inputId", "readonly", "trueValue", "falseValue", "ariaLabel", "size", "ariaLabelledBy", "autofocus"], outputs: ["onChange"] }, { kind: "component", type: 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: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
4894
5784
  }
4895
5785
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ConfigComponent, decorators: [{
4896
5786
  type: Component,
@@ -5015,11 +5905,48 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
5015
5905
  }]
5016
5906
  }], ctorParameters: () => [] });
5017
5907
 
5908
+ var en$3 = {
5909
+ "db-migration": {
5910
+ migrationManager: "Migration manager",
5911
+ actions: {
5912
+ refresh: "Refresh",
5913
+ cleanFilter: "Clean filter",
5914
+ applyMigration: "Apply migration"
5915
+ },
5916
+ columns: {
5917
+ name: "Migration name",
5918
+ applied: "Applied",
5919
+ exist: "Exist",
5920
+ pending: "Pending"
5921
+ },
5922
+ messages: {
5923
+ errorRefreshing: "Error refreshing database"
5924
+ }
5925
+ }
5926
+ };
5927
+
5928
+ var ru$3 = {
5929
+ "db-migration": {
5930
+ migrationManager: "Менеджер миграций",
5931
+ actions: {
5932
+ refresh: "Обновить",
5933
+ cleanFilter: "Очистить фильтр",
5934
+ applyMigration: "Применить миграцию"
5935
+ },
5936
+ columns: {
5937
+ name: "Название миграции",
5938
+ applied: "Применена",
5939
+ exist: "Существует",
5940
+ pending: "Ожидает"
5941
+ },
5942
+ messages: {
5943
+ errorRefreshing: "Ошибка обновления базы данных"
5944
+ }
5945
+ }
5946
+ };
5947
+
5018
5948
  class DbMigrationComponent extends BaseModuleComponent {
5019
- constructor() {
5020
- super();
5021
- this.l10nService.loadComponentTranslations('db-migration');
5022
- }
5949
+ static { this.translations = { en: en$3, ru: ru$3 }; }
5023
5950
  async ngOnInit() {
5024
5951
  await super.ngOnInit();
5025
5952
  await this.refreshAction();
@@ -5041,11 +5968,11 @@ class DbMigrationComponent extends BaseModuleComponent {
5041
5968
  const request = { name: rowData.name };
5042
5969
  return this.applyModuleMigration(request);
5043
5970
  }
5044
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: DbMigrationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5971
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: DbMigrationComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
5045
5972
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.24", type: DbMigrationComponent, isStandalone: true, selector: "db-migration", providers: [ConfirmationService], usesInheritance: true, ngImport: i0, template: `
5046
5973
  @if (isContent) {
5047
5974
  <div class="card" style="height: 100%">
5048
- <p-confirmDialog/>
5975
+ <p-confirmDialog />
5049
5976
  <div>
5050
5977
  <h5>{{ 'db-migration.migrationManager' | translate }}</h5>
5051
5978
  <div class="flex flex-row gap-2">
@@ -5055,14 +5982,14 @@ class DbMigrationComponent extends BaseModuleComponent {
5055
5982
  tooltipPosition="bottom"
5056
5983
  [outlined]="true"
5057
5984
  [pTooltip]="'db-migration.actions.refresh' | translate"
5058
- (click)="refreshAction()"/>
5985
+ (click)="refreshAction()" />
5059
5986
  <p-button
5060
5987
  icon="pi pi-filter-slash"
5061
5988
  severity="secondary"
5062
5989
  tooltipPosition="bottom"
5063
5990
  [outlined]="true"
5064
5991
  [pTooltip]="'db-migration.actions.cleanFilter' | translate"
5065
- (click)="dt.clear()"/>
5992
+ (click)="dt.clear()" />
5066
5993
  </div>
5067
5994
  <div>
5068
5995
  <p-table #dt dataKey="name" editMode="row" size="small" [scrollable]="true" [value]="data">
@@ -5070,7 +5997,7 @@ class DbMigrationComponent extends BaseModuleComponent {
5070
5997
  <tr>
5071
5998
  <th pSortableColumn="name" scope="col">
5072
5999
  {{ 'db-migration.columns.name' | translate }}
5073
- <p-columnFilter display="menu" field="name" type="text"/>
6000
+ <p-columnFilter display="menu" field="name" type="text" />
5074
6001
  </th>
5075
6002
  <th scope="col">{{ 'db-migration.columns.applied' | translate }}</th>
5076
6003
  <th scope="col">{{ 'db-migration.columns.exist' | translate }}</th>
@@ -5091,7 +6018,7 @@ class DbMigrationComponent extends BaseModuleComponent {
5091
6018
  </td>
5092
6019
  <td>
5093
6020
  @if (rowData.exist) {
5094
- <p-button icon="pi pi-check" severity="success" [rounded]="true" [text]="true"/>
6021
+ <p-button icon="pi pi-check" severity="success" [rounded]="true" [text]="true" />
5095
6022
  }
5096
6023
  </td>
5097
6024
  <td>
@@ -5118,9 +6045,9 @@ class DbMigrationComponent extends BaseModuleComponent {
5118
6045
  </div>
5119
6046
  </div>
5120
6047
  } @else if (isSecurity) {
5121
- <security [controller]="controller" [id]="id"/>
6048
+ <security [controller]="controller" [id]="id" />
5122
6049
  }
5123
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: TableModule }, { kind: "component", type: i1$8.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$2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i1$8.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "directive", type: i1$8.EditableRow, selector: "[pEditableRow]", inputs: ["pEditableRow", "pEditableRowDisabled"] }, { kind: "directive", type: i1$8.CancelEditableRow, selector: "[pCancelEditableRow]" }, { kind: "component", type: i1$8.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" }] }); }
6050
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: TableModule }, { kind: "component", type: i1$8.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$8.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "directive", type: i1$8.EditableRow, selector: "[pEditableRow]", inputs: ["pEditableRow", "pEditableRowDisabled"] }, { kind: "directive", type: i1$8.CancelEditableRow, selector: "[pCancelEditableRow]" }, { kind: "component", type: i1$8.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" }] }); }
5124
6051
  }
5125
6052
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: DbMigrationComponent, decorators: [{
5126
6053
  type: Component,
@@ -5142,7 +6069,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
5142
6069
  template: `
5143
6070
  @if (isContent) {
5144
6071
  <div class="card" style="height: 100%">
5145
- <p-confirmDialog/>
6072
+ <p-confirmDialog />
5146
6073
  <div>
5147
6074
  <h5>{{ 'db-migration.migrationManager' | translate }}</h5>
5148
6075
  <div class="flex flex-row gap-2">
@@ -5152,14 +6079,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
5152
6079
  tooltipPosition="bottom"
5153
6080
  [outlined]="true"
5154
6081
  [pTooltip]="'db-migration.actions.refresh' | translate"
5155
- (click)="refreshAction()"/>
6082
+ (click)="refreshAction()" />
5156
6083
  <p-button
5157
6084
  icon="pi pi-filter-slash"
5158
6085
  severity="secondary"
5159
6086
  tooltipPosition="bottom"
5160
6087
  [outlined]="true"
5161
6088
  [pTooltip]="'db-migration.actions.cleanFilter' | translate"
5162
- (click)="dt.clear()"/>
6089
+ (click)="dt.clear()" />
5163
6090
  </div>
5164
6091
  <div>
5165
6092
  <p-table #dt dataKey="name" editMode="row" size="small" [scrollable]="true" [value]="data">
@@ -5167,7 +6094,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
5167
6094
  <tr>
5168
6095
  <th pSortableColumn="name" scope="col">
5169
6096
  {{ 'db-migration.columns.name' | translate }}
5170
- <p-columnFilter display="menu" field="name" type="text"/>
6097
+ <p-columnFilter display="menu" field="name" type="text" />
5171
6098
  </th>
5172
6099
  <th scope="col">{{ 'db-migration.columns.applied' | translate }}</th>
5173
6100
  <th scope="col">{{ 'db-migration.columns.exist' | translate }}</th>
@@ -5188,7 +6115,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
5188
6115
  </td>
5189
6116
  <td>
5190
6117
  @if (rowData.exist) {
5191
- <p-button icon="pi pi-check" severity="success" [rounded]="true" [text]="true"/>
6118
+ <p-button icon="pi pi-check" severity="success" [rounded]="true" [text]="true" />
5192
6119
  }
5193
6120
  </td>
5194
6121
  <td>
@@ -5215,12 +6142,64 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
5215
6142
  </div>
5216
6143
  </div>
5217
6144
  } @else if (isSecurity) {
5218
- <security [controller]="controller" [id]="id"/>
6145
+ <security [controller]="controller" [id]="id" />
5219
6146
  }
5220
6147
  `,
5221
6148
  providers: [ConfirmationService]
5222
6149
  }]
5223
- }], ctorParameters: () => [] });
6150
+ }] });
6151
+
6152
+ /* eslint-disable */
6153
+ /* tslint:disable */
6154
+ // @ts-nocheck
6155
+ class ExtensionModulesApi extends HttpClient {
6156
+ constructor() {
6157
+ super(...arguments);
6158
+ this.getExtensionModules = (params = {}) => this.request({
6159
+ path: `/api/extension-modules/get-extension-modules`,
6160
+ method: "GET",
6161
+ secure: true,
6162
+ format: "json",
6163
+ ...params,
6164
+ });
6165
+ this.getExtensionModuleByKey = ({ extensionKey, ...query }, params = {}) => this.request({
6166
+ path: `/api/extension-modules/get-extension-module-by-key/${extensionKey}`,
6167
+ method: "GET",
6168
+ secure: true,
6169
+ format: "json",
6170
+ ...params,
6171
+ });
6172
+ this.registerExtensionModule = (data, params = {}) => this.request({
6173
+ path: `/api/extension-modules/register-extension-module`,
6174
+ method: "POST",
6175
+ body: data,
6176
+ secure: true,
6177
+ type: ContentType.Json,
6178
+ format: "json",
6179
+ ...params,
6180
+ });
6181
+ this.updateExtensionModule = ({ id, ...query }, data, params = {}) => this.request({
6182
+ path: `/api/extension-modules/update-extension-module/${id}`,
6183
+ method: "PUT",
6184
+ body: data,
6185
+ secure: true,
6186
+ type: ContentType.Json,
6187
+ format: "json",
6188
+ ...params,
6189
+ });
6190
+ this.deleteExtensionModule = ({ id, ...query }, params = {}) => this.request({
6191
+ path: `/api/extension-modules/delete-extension-module/${id}`,
6192
+ method: "DELETE",
6193
+ secure: true,
6194
+ ...params,
6195
+ });
6196
+ }
6197
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ExtensionModulesApi, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
6198
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ExtensionModulesApi }); }
6199
+ }
6200
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ExtensionModulesApi, decorators: [{
6201
+ type: Injectable
6202
+ }] });
5224
6203
 
5225
6204
  /* eslint-disable */
5226
6205
  /* tslint:disable */
@@ -5266,6 +6245,69 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
5266
6245
  type: Injectable
5267
6246
  }] });
5268
6247
 
6248
+ var en$2 = {
6249
+ "app-modules": {
6250
+ title: "Modules",
6251
+ refreshTooltip: "Refresh",
6252
+ register: {
6253
+ manifestUrlPlaceholder: "External module manifest URL",
6254
+ button: "Register"
6255
+ },
6256
+ table: {
6257
+ moduleId: "Module ID",
6258
+ name: "Name",
6259
+ currentlyLoaded: "Currently Loaded",
6260
+ yes: "Yes",
6261
+ no: "No",
6262
+ deleteTooltip: "Delete"
6263
+ },
6264
+ confirm: {
6265
+ header: "Warning",
6266
+ message: "Are you sure you want to delete the module?",
6267
+ cancel: "Cancel",
6268
+ "delete": "Delete"
6269
+ },
6270
+ messages: {
6271
+ deleteSuccess: "Module deleted",
6272
+ deleteError: "Module delete failed",
6273
+ registerError: "External module registration failed",
6274
+ registerSuccess: "External module registered"
6275
+ }
6276
+ }
6277
+ };
6278
+
6279
+ var ru$2 = {
6280
+ "app-modules": {
6281
+ title: "Модули",
6282
+ refreshTooltip: "Обновить",
6283
+ register: {
6284
+ manifestUrlPlaceholder: "URL манифеста внешнего модуля",
6285
+ button: "Зарегистрировать"
6286
+ },
6287
+ table: {
6288
+ moduleId: "ID модуля",
6289
+ name: "Название",
6290
+ currentlyLoaded: "Загружен",
6291
+ yes: "Да",
6292
+ no: "Нет",
6293
+ deleteTooltip: "Удалить"
6294
+ },
6295
+ confirm: {
6296
+ header: "Внимание",
6297
+ message: "Вы уверены, что хотите удалить модуль?",
6298
+ cancel: "Отмена",
6299
+ "delete": "Удалить"
6300
+ },
6301
+ messages: {
6302
+ deleteSuccess: "Модуль удален",
6303
+ deleteError: "Не удалось удалить модуль",
6304
+ registerError: "Не удалось зарегистрировать внешний модуль",
6305
+ registerSuccess: "Внешний модуль зарегистрирован"
6306
+ }
6307
+ }
6308
+ };
6309
+
6310
+ L10nService.registerTranslations({ en: en$2, ru: ru$2 });
5269
6311
  class AppModulesComponent {
5270
6312
  constructor() {
5271
6313
  this.modules = [];
@@ -5273,7 +6315,10 @@ class AppModulesComponent {
5273
6315
  this.confirmationService = inject(ConfirmationService);
5274
6316
  this.l10nService = inject(L10nService);
5275
6317
  this.titleService = inject(AppTitleService);
6318
+ this.externalModuleManifestUrl = '';
6319
+ this.registeringExternalModule = false;
5276
6320
  this.moduleService = inject(ModuleApi);
6321
+ this.extensionModulesService = inject(ExtensionModulesApi);
5277
6322
  this.translationsReady = firstValueFrom(this.l10nService.loadComponentTranslations('app-modules'));
5278
6323
  }
5279
6324
  async ngOnInit() {
@@ -5284,6 +6329,30 @@ class AppModulesComponent {
5284
6329
  async refreshAction() {
5285
6330
  this.modules = await this.moduleService.getModulesWithLoadStatus();
5286
6331
  }
6332
+ get canRegisterExternalModule() {
6333
+ return !!this.externalModuleManifestUrl.trim() && !this.registeringExternalModule;
6334
+ }
6335
+ async registerExternalModule() {
6336
+ if (!this.canRegisterExternalModule) {
6337
+ return;
6338
+ }
6339
+ await this.translationsReady;
6340
+ this.registeringExternalModule = true;
6341
+ try {
6342
+ await this.extensionModulesService.registerExtensionModule({
6343
+ manifestUrl: this.externalModuleManifestUrl.trim()
6344
+ });
6345
+ this.externalModuleManifestUrl = '';
6346
+ await this.refreshAction();
6347
+ this.msgService.success(this.t('app-modules.messages.registerSuccess'));
6348
+ }
6349
+ catch (error) {
6350
+ this.msgService.errorFromException(error, this.t('app-modules.messages.registerError'), this.t('app-modules.messages.registerError'));
6351
+ }
6352
+ finally {
6353
+ this.registeringExternalModule = false;
6354
+ }
6355
+ }
5287
6356
  async deleteModule(module) {
5288
6357
  await this.translationsReady;
5289
6358
  this.confirmationService.confirm({
@@ -5308,7 +6377,7 @@ class AppModulesComponent {
5308
6377
  this.msgService.success(this.t('app-modules.messages.deleteSuccess'));
5309
6378
  }
5310
6379
  catch (error) {
5311
- this.msgService.error(error);
6380
+ this.msgService.errorFromException(error, this.t('app-modules.messages.deleteError'), this.t('app-modules.messages.deleteError'));
5312
6381
  }
5313
6382
  }
5314
6383
  });
@@ -5317,7 +6386,7 @@ class AppModulesComponent {
5317
6386
  return this.l10nService.instant(key);
5318
6387
  }
5319
6388
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: AppModulesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5320
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.24", type: AppModulesComponent, isStandalone: true, selector: "app-modules", providers: [ConfirmationService, ModuleApi], ngImport: i0, template: `
6389
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.24", type: AppModulesComponent, isStandalone: true, selector: "app-modules", providers: [ConfirmationService, ModuleApi, ExtensionModulesApi], ngImport: i0, template: `
5321
6390
  <p-confirmDialog></p-confirmDialog>
5322
6391
  <div class="flex flex-col md:flex-row gap-4">
5323
6392
  <div class="card w-full">
@@ -5326,14 +6395,33 @@ class AppModulesComponent {
5326
6395
  </div>
5327
6396
  <div class="mb-4">
5328
6397
  <p-toolbar>
5329
- <p-button
5330
- icon="pi pi-refresh"
5331
- rounded="true"
5332
- severity="secondary"
5333
- text="true"
5334
- tooltipPosition="bottom"
5335
- [pTooltip]="'app-modules.refreshTooltip' | translate"
5336
- (onClick)="refreshAction()"></p-button>
6398
+ <div class="flex flex-col md:flex-row md:items-center gap-2 w-full">
6399
+ <div class="flex flex-col sm:flex-row gap-2 flex-1">
6400
+ <input
6401
+ class="w-full"
6402
+ pInputText
6403
+ type="url"
6404
+ [disabled]="registeringExternalModule"
6405
+ [placeholder]="'app-modules.register.manifestUrlPlaceholder' | translate"
6406
+ [(ngModel)]="externalModuleManifestUrl"
6407
+ (keydown.enter)="registerExternalModule()" />
6408
+ <p-button
6409
+ icon="pi pi-plus"
6410
+ severity="success"
6411
+ [disabled]="!canRegisterExternalModule"
6412
+ [label]="'app-modules.register.button' | translate"
6413
+ [loading]="registeringExternalModule"
6414
+ (onClick)="registerExternalModule()"></p-button>
6415
+ </div>
6416
+ <p-button
6417
+ icon="pi pi-refresh"
6418
+ rounded="true"
6419
+ severity="secondary"
6420
+ text="true"
6421
+ tooltipPosition="bottom"
6422
+ [pTooltip]="'app-modules.refreshTooltip' | translate"
6423
+ (onClick)="refreshAction()"></p-button>
6424
+ </div>
5337
6425
  </p-toolbar>
5338
6426
  </div>
5339
6427
  <p-table class="mt-4" [paginator]="true" [rows]="100" [value]="modules">
@@ -5371,13 +6459,23 @@ class AppModulesComponent {
5371
6459
  </p-table>
5372
6460
  </div>
5373
6461
  </div>
5374
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i1$8.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$2.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$2.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" }] }); }
6462
+ `, isInline: true, dependencies: [{ 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: TableModule }, { kind: "component", type: i1$8.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: i5$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: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pSize", "variant", "fluid", "invalid"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
5375
6463
  }
5376
6464
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: AppModulesComponent, decorators: [{
5377
6465
  type: Component,
5378
6466
  args: [{
5379
- imports: [FormsModule, TableModule, Tag, ButtonModule, ToolbarModule, Tooltip, ConfirmDialog, TranslatePipe],
5380
- providers: [ConfirmationService, ModuleApi],
6467
+ imports: [
6468
+ FormsModule,
6469
+ TableModule,
6470
+ Tag,
6471
+ ButtonModule,
6472
+ ToolbarModule,
6473
+ Tooltip,
6474
+ ConfirmDialog,
6475
+ TranslatePipe,
6476
+ InputTextModule
6477
+ ],
6478
+ providers: [ConfirmationService, ModuleApi, ExtensionModulesApi],
5381
6479
  selector: 'app-modules',
5382
6480
  template: `
5383
6481
  <p-confirmDialog></p-confirmDialog>
@@ -5388,14 +6486,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
5388
6486
  </div>
5389
6487
  <div class="mb-4">
5390
6488
  <p-toolbar>
5391
- <p-button
5392
- icon="pi pi-refresh"
5393
- rounded="true"
5394
- severity="secondary"
5395
- text="true"
5396
- tooltipPosition="bottom"
5397
- [pTooltip]="'app-modules.refreshTooltip' | translate"
5398
- (onClick)="refreshAction()"></p-button>
6489
+ <div class="flex flex-col md:flex-row md:items-center gap-2 w-full">
6490
+ <div class="flex flex-col sm:flex-row gap-2 flex-1">
6491
+ <input
6492
+ class="w-full"
6493
+ pInputText
6494
+ type="url"
6495
+ [disabled]="registeringExternalModule"
6496
+ [placeholder]="'app-modules.register.manifestUrlPlaceholder' | translate"
6497
+ [(ngModel)]="externalModuleManifestUrl"
6498
+ (keydown.enter)="registerExternalModule()" />
6499
+ <p-button
6500
+ icon="pi pi-plus"
6501
+ severity="success"
6502
+ [disabled]="!canRegisterExternalModule"
6503
+ [label]="'app-modules.register.button' | translate"
6504
+ [loading]="registeringExternalModule"
6505
+ (onClick)="registerExternalModule()"></p-button>
6506
+ </div>
6507
+ <p-button
6508
+ icon="pi pi-refresh"
6509
+ rounded="true"
6510
+ severity="secondary"
6511
+ text="true"
6512
+ tooltipPosition="bottom"
6513
+ [pTooltip]="'app-modules.refreshTooltip' | translate"
6514
+ (onClick)="refreshAction()"></p-button>
6515
+ </div>
5399
6516
  </p-toolbar>
5400
6517
  </div>
5401
6518
  <p-table class="mt-4" [paginator]="true" [rows]="100" [value]="modules">
@@ -5443,6 +6560,103 @@ var ServiceType;
5443
6560
  ServiceType["Application"] = "Application";
5444
6561
  })(ServiceType || (ServiceType = {}));
5445
6562
 
6563
+ var applications$1 = {
6564
+ title: "Applications",
6565
+ subtitle: "Manage the frontend application registry",
6566
+ searchPlaceholder: "Search applications",
6567
+ search: "Search",
6568
+ clear: "Clear",
6569
+ add: "Add",
6570
+ refreshTooltip: "Refresh",
6571
+ empty: "No applications found",
6572
+ table: {
6573
+ code: "Code",
6574
+ displayName: "Name",
6575
+ baseUrl: "Base URL",
6576
+ internalBaseUrl: "Internal Base URL",
6577
+ icon: "Icon",
6578
+ order: "Order",
6579
+ enabled: "Enabled",
6580
+ serviceType: "Service type",
6581
+ current: "Current",
6582
+ actions: "Actions",
6583
+ yes: "Yes",
6584
+ no: "No",
6585
+ editTooltip: "Edit",
6586
+ deleteTooltip: "Delete",
6587
+ saveTooltip: "Save",
6588
+ cancelTooltip: "Cancel"
6589
+ },
6590
+ serviceTypes: {
6591
+ service: "Service",
6592
+ application: "Application"
6593
+ },
6594
+ confirm: {
6595
+ header: "Warning",
6596
+ message: "Are you sure you want to delete application {{displayName}}?",
6597
+ cancel: "Cancel",
6598
+ "delete": "Delete"
6599
+ },
6600
+ messages: {
6601
+ requiredFields: "Fill code, name and Base URL",
6602
+ createSuccess: "Application created",
6603
+ updateSuccess: "Application updated",
6604
+ deleteSuccess: "Application deleted"
6605
+ }
6606
+ };
6607
+ var en$1 = {
6608
+ applications: applications$1
6609
+ };
6610
+
6611
+ var applications = {
6612
+ title: "Приложения",
6613
+ subtitle: "Управление реестром фронтенд-приложений",
6614
+ searchPlaceholder: "Поиск по приложениям",
6615
+ search: "Найти",
6616
+ clear: "Очистить",
6617
+ add: "Добавить",
6618
+ refreshTooltip: "Обновить",
6619
+ empty: "Приложения не найдены",
6620
+ table: {
6621
+ code: "Код",
6622
+ displayName: "Название",
6623
+ baseUrl: "Base URL",
6624
+ internalBaseUrl: "Internal Base URL",
6625
+ icon: "Иконка",
6626
+ order: "Порядок",
6627
+ enabled: "Включено",
6628
+ serviceType: "Тип сервиса",
6629
+ current: "Текущее",
6630
+ actions: "Действия",
6631
+ yes: "Да",
6632
+ no: "Нет",
6633
+ editTooltip: "Редактировать",
6634
+ deleteTooltip: "Удалить",
6635
+ saveTooltip: "Сохранить",
6636
+ cancelTooltip: "Отменить"
6637
+ },
6638
+ serviceTypes: {
6639
+ service: "Сервис",
6640
+ application: "Приложение"
6641
+ },
6642
+ confirm: {
6643
+ header: "Внимание",
6644
+ message: "Вы уверены, что хотите удалить приложение {{displayName}}?",
6645
+ cancel: "Отмена",
6646
+ "delete": "Удалить"
6647
+ },
6648
+ messages: {
6649
+ requiredFields: "Заполните код, название и Base URL",
6650
+ createSuccess: "Приложение создано",
6651
+ updateSuccess: "Приложение обновлено",
6652
+ deleteSuccess: "Приложение удалено"
6653
+ }
6654
+ };
6655
+ var ru$1 = {
6656
+ applications: applications
6657
+ };
6658
+
6659
+ L10nService.registerTranslations({ en: en$1, ru: ru$1 });
5446
6660
  class ApplicationsComponent {
5447
6661
  get serviceTypeOptions() {
5448
6662
  return [
@@ -5649,9 +6863,7 @@ class ApplicationsComponent {
5649
6863
  this.visibleApplications = this.filterApplications(this.globalFilter);
5650
6864
  }
5651
6865
  filterApplications(filter) {
5652
- const source = this.draftApplication
5653
- ? [this.draftApplication, ...this.applications]
5654
- : [...this.applications];
6866
+ const source = this.draftApplication ? [this.draftApplication, ...this.applications] : [...this.applications];
5655
6867
  const normalizedFilter = filter.trim().toLocaleLowerCase();
5656
6868
  if (!normalizedFilter) {
5657
6869
  return source;
@@ -5750,7 +6962,7 @@ class ApplicationsComponent {
5750
6962
  {{ 'applications.table.icon' | translate }}
5751
6963
  <p-sortIcon field="icon"></p-sortIcon>
5752
6964
  </th>
5753
- <th pSortableColumn="order" class="text-right">
6965
+ <th class="text-right" pSortableColumn="order">
5754
6966
  {{ 'applications.table.order' | translate }}
5755
6967
  <p-sortIcon field="order"></p-sortIcon>
5756
6968
  </th>
@@ -5823,16 +7035,16 @@ class ApplicationsComponent {
5823
7035
  [severity]="application.enabled ? 'success' : 'danger'"
5824
7036
  [value]="
5825
7037
  (application.enabled ? 'applications.table.yes' : 'applications.table.no') | translate
5826
- "></p-tag>
7038
+ "></p-tag>
5827
7039
  }
5828
7040
  </td>
5829
7041
  <td>
5830
7042
  @if (application._isEditing && application._editModel; as editModel) {
5831
7043
  <p-select
7044
+ appendTo="body"
5832
7045
  class="w-full min-w-36"
5833
7046
  optionLabel="label"
5834
7047
  optionValue="value"
5835
- appendTo="body"
5836
7048
  [options]="serviceTypeOptions"
5837
7049
  [(ngModel)]="editModel.serviceType"></p-select>
5838
7050
  } @else {
@@ -5853,33 +7065,33 @@ class ApplicationsComponent {
5853
7065
  icon="pi pi-check"
5854
7066
  severity="success"
5855
7067
  text="true"
7068
+ tooltipPosition="bottom"
5856
7069
  [disabled]="activeRowAction"
5857
7070
  [pTooltip]="'applications.table.saveTooltip' | translate"
5858
- tooltipPosition="bottom"
5859
7071
  (onClick)="saveApplication(application)"></p-button>
5860
7072
  <p-button
5861
7073
  icon="pi pi-times"
5862
7074
  severity="secondary"
5863
7075
  text="true"
7076
+ tooltipPosition="bottom"
5864
7077
  [disabled]="activeRowAction"
5865
7078
  [pTooltip]="'applications.table.cancelTooltip' | translate"
5866
- tooltipPosition="bottom"
5867
7079
  (onClick)="cancelEdit(application)"></p-button>
5868
7080
  } @else {
5869
7081
  <p-button
5870
7082
  icon="pi pi-pencil"
5871
7083
  text="true"
7084
+ tooltipPosition="bottom"
5872
7085
  [disabled]="activeRowAction"
5873
7086
  [pTooltip]="'applications.table.editTooltip' | translate"
5874
- tooltipPosition="bottom"
5875
7087
  (onClick)="editApplication(application)"></p-button>
5876
7088
  <p-button
5877
7089
  icon="pi pi-trash"
5878
7090
  severity="danger"
5879
7091
  text="true"
7092
+ tooltipPosition="bottom"
5880
7093
  [disabled]="activeRowAction"
5881
7094
  [pTooltip]="'applications.table.deleteTooltip' | translate"
5882
- tooltipPosition="bottom"
5883
7095
  (onClick)="deleteApplication(application)"></p-button>
5884
7096
  }
5885
7097
  </div>
@@ -5889,7 +7101,7 @@ class ApplicationsComponent {
5889
7101
 
5890
7102
  <ng-template pTemplate="emptymessage">
5891
7103
  <tr>
5892
- <td colspan="10" class="py-8 text-center text-surface-500">
7104
+ <td class="py-8 text-center text-surface-500" colspan="10">
5893
7105
  {{ 'applications.empty' | translate }}
5894
7106
  </td>
5895
7107
  </tr>
@@ -5897,7 +7109,7 @@ class ApplicationsComponent {
5897
7109
  </p-table>
5898
7110
  </div>
5899
7111
  </div>
5900
- `, isInline: true, dependencies: [{ 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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { 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: TableModule }, { kind: "component", type: i1$8.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$2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i1$8.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "component", type: i1$8.SortIcon, selector: "p-sortIcon", inputs: ["field"] }, { 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$2.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: "directive", type: 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: ToggleSwitchModule }, { kind: "component", type: i2$4.ToggleSwitch, selector: "p-toggleswitch, p-toggleSwitch, p-toggle-switch", inputs: ["styleClass", "tabindex", "inputId", "readonly", "trueValue", "falseValue", "ariaLabel", "size", "ariaLabelledBy", "autofocus"], outputs: ["onChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
7112
+ `, isInline: true, dependencies: [{ 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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { 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: TableModule }, { kind: "component", type: i1$8.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$8.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "component", type: i1$8.SortIcon, selector: "p-sortIcon", inputs: ["field"] }, { 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: i5$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: "directive", type: 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: ToggleSwitchModule }, { kind: "component", type: i2$5.ToggleSwitch, selector: "p-toggleswitch, p-toggleSwitch, p-toggle-switch", inputs: ["styleClass", "tabindex", "inputId", "readonly", "trueValue", "falseValue", "ariaLabel", "size", "ariaLabelledBy", "autofocus"], outputs: ["onChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
5901
7113
  }
5902
7114
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ApplicationsComponent, decorators: [{
5903
7115
  type: Component,
@@ -6000,7 +7212,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
6000
7212
  {{ 'applications.table.icon' | translate }}
6001
7213
  <p-sortIcon field="icon"></p-sortIcon>
6002
7214
  </th>
6003
- <th pSortableColumn="order" class="text-right">
7215
+ <th class="text-right" pSortableColumn="order">
6004
7216
  {{ 'applications.table.order' | translate }}
6005
7217
  <p-sortIcon field="order"></p-sortIcon>
6006
7218
  </th>
@@ -6073,16 +7285,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
6073
7285
  [severity]="application.enabled ? 'success' : 'danger'"
6074
7286
  [value]="
6075
7287
  (application.enabled ? 'applications.table.yes' : 'applications.table.no') | translate
6076
- "></p-tag>
7288
+ "></p-tag>
6077
7289
  }
6078
7290
  </td>
6079
7291
  <td>
6080
7292
  @if (application._isEditing && application._editModel; as editModel) {
6081
7293
  <p-select
7294
+ appendTo="body"
6082
7295
  class="w-full min-w-36"
6083
7296
  optionLabel="label"
6084
7297
  optionValue="value"
6085
- appendTo="body"
6086
7298
  [options]="serviceTypeOptions"
6087
7299
  [(ngModel)]="editModel.serviceType"></p-select>
6088
7300
  } @else {
@@ -6103,33 +7315,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
6103
7315
  icon="pi pi-check"
6104
7316
  severity="success"
6105
7317
  text="true"
7318
+ tooltipPosition="bottom"
6106
7319
  [disabled]="activeRowAction"
6107
7320
  [pTooltip]="'applications.table.saveTooltip' | translate"
6108
- tooltipPosition="bottom"
6109
7321
  (onClick)="saveApplication(application)"></p-button>
6110
7322
  <p-button
6111
7323
  icon="pi pi-times"
6112
7324
  severity="secondary"
6113
7325
  text="true"
7326
+ tooltipPosition="bottom"
6114
7327
  [disabled]="activeRowAction"
6115
7328
  [pTooltip]="'applications.table.cancelTooltip' | translate"
6116
- tooltipPosition="bottom"
6117
7329
  (onClick)="cancelEdit(application)"></p-button>
6118
7330
  } @else {
6119
7331
  <p-button
6120
7332
  icon="pi pi-pencil"
6121
7333
  text="true"
7334
+ tooltipPosition="bottom"
6122
7335
  [disabled]="activeRowAction"
6123
7336
  [pTooltip]="'applications.table.editTooltip' | translate"
6124
- tooltipPosition="bottom"
6125
7337
  (onClick)="editApplication(application)"></p-button>
6126
7338
  <p-button
6127
7339
  icon="pi pi-trash"
6128
7340
  severity="danger"
6129
7341
  text="true"
7342
+ tooltipPosition="bottom"
6130
7343
  [disabled]="activeRowAction"
6131
7344
  [pTooltip]="'applications.table.deleteTooltip' | translate"
6132
- tooltipPosition="bottom"
6133
7345
  (onClick)="deleteApplication(application)"></p-button>
6134
7346
  }
6135
7347
  </div>
@@ -6139,7 +7351,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
6139
7351
 
6140
7352
  <ng-template pTemplate="emptymessage">
6141
7353
  <tr>
6142
- <td colspan="10" class="py-8 text-center text-surface-500">
7354
+ <td class="py-8 text-center text-surface-500" colspan="10">
6143
7355
  {{ 'applications.empty' | translate }}
6144
7356
  </td>
6145
7357
  </tr>
@@ -6468,7 +7680,9 @@ class DiscussionComponent {
6468
7680
  deleteAttachment(comment, attachment) {
6469
7681
  this.confirmationService.confirm({
6470
7682
  header: this.translateService.instant('discussionComponent.warning'),
6471
- message: this.translateService.instant('discussionComponent.confirmDeleteFile', { fileName: attachment.fileName }),
7683
+ message: this.translateService.instant('discussionComponent.confirmDeleteFile', {
7684
+ fileName: attachment.fileName
7685
+ }),
6472
7686
  icon: 'pi pi-trash',
6473
7687
  rejectButtonProps: {
6474
7688
  label: this.translateService.instant('discussionComponent.cancel'),
@@ -6671,27 +7885,24 @@ class DiscussionComponent {
6671
7885
  }
6672
7886
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: DiscussionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6673
7887
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.24", type: DiscussionComponent, isStandalone: true, selector: "discussion", inputs: { objectTypeId: "objectTypeId", objectId: "objectId" }, providers: [ConfirmationService, DiscussionApi], usesOnChanges: true, ngImport: i0, template: `
6674
- <p-confirmDialog/>
7888
+ <p-confirmDialog />
6675
7889
 
6676
7890
  <section class="flex flex-col gap-4">
6677
-
6678
7891
  <p-card class="block">
6679
7892
  <div class="flex flex-col gap-4">
6680
7893
  @if (!previewMode) {
6681
7894
  <textarea
6682
- pTextarea
6683
7895
  class="min-h-28 w-full"
6684
- [autoResize]="true"
7896
+ pTextarea
6685
7897
  rows="5"
6686
- [(ngModel)]="newComment"
6687
- (ngModelChange)="onComposerInput()"
7898
+ [autoResize]="true"
6688
7899
  [placeholder]="'discussionComponent.writeCommentPlaceholder' | translate"
6689
- ></textarea>
7900
+ [(ngModel)]="newComment"
7901
+ (ngModelChange)="onComposerInput()"></textarea>
6690
7902
  } @else {
6691
7903
  <div
6692
7904
  class="markdown-preview min-h-28 rounded-2xl border border-surface-200 bg-surface-50 px-4 py-3 leading-7 dark:border-surface-700 dark:bg-surface-900/40"
6693
- [innerHTML]="renderMarkdown(newComment)"
6694
- ></div>
7905
+ [innerHTML]="renderMarkdown(newComment)"></div>
6695
7906
  }
6696
7907
 
6697
7908
  @if (mentionSuggestions.length > 0) {
@@ -6706,10 +7917,9 @@ class DiscussionComponent {
6706
7917
  <div class="flex flex-col gap-3">
6707
7918
  @for (candidate of mentionSuggestions; track candidate.userId) {
6708
7919
  <button
6709
- type="button"
6710
7920
  class="flex w-full items-center justify-between gap-4 rounded-xl border border-transparent px-3 py-3 text-left transition hover:border-primary-200 hover:bg-primary-50 dark:hover:border-primary-800 dark:hover:bg-primary-950/30"
6711
- (click)="insertMention(candidate)"
6712
- >
7921
+ type="button"
7922
+ (click)="insertMention(candidate)">
6713
7923
  <div class="min-w-0">
6714
7924
  <strong>{{ candidate.displayName }}</strong>
6715
7925
  <small class="mt-1 block text-surface-500">{{ candidate.email }}</small>
@@ -6729,15 +7939,14 @@ class DiscussionComponent {
6729
7939
  <div class="inline-flex items-center gap-2">
6730
7940
  <i class="pi pi-file"></i>
6731
7941
  <span class="break-all">{{ file.name }}</span>
6732
- <p-tag [value]="formatBytes(file.size)" severity="contrast"/>
7942
+ <p-tag severity="contrast" [value]="formatBytes(file.size)" />
6733
7943
  </div>
6734
7944
  <p-button
6735
7945
  icon="pi pi-times"
6736
7946
  severity="danger"
6737
7947
  [rounded]="true"
6738
7948
  [text]="true"
6739
- (onClick)="removePendingFile(file)"
6740
- />
7949
+ (onClick)="removePendingFile(file)" />
6741
7950
  </div>
6742
7951
  }
6743
7952
  </div>
@@ -6745,33 +7954,28 @@ class DiscussionComponent {
6745
7954
 
6746
7955
  <div class="flex flex-col gap-2 sm:flex-row sm:justify-end">
6747
7956
  <p-button
7957
+ severity="secondary"
7958
+ [icon]="previewMode ? 'pi pi-pencil' : 'pi pi-eye'"
6748
7959
  [label]="
6749
- previewMode
6750
- ? ('discussionComponent.edit' | translate)
6751
- : ('discussionComponent.preview' | translate)
7960
+ previewMode ? ('discussionComponent.edit' | translate) : ('discussionComponent.preview' | translate)
6752
7961
  "
6753
- [icon]="previewMode ? 'pi pi-pencil' : 'pi pi-eye'"
6754
- severity="secondary"
6755
7962
  [text]="true"
6756
- (onClick)="previewMode = !previewMode"
6757
- />
7963
+ (onClick)="previewMode = !previewMode" />
6758
7964
 
6759
7965
  <label class="file-trigger">
6760
7966
  <p-button
6761
- [label]="'discussionComponent.attachFiles' | translate"
6762
7967
  icon="pi pi-paperclip"
6763
7968
  severity="secondary"
6764
- [text]="true"
6765
- />
6766
- <input type="file" multiple (change)="onFilesSelected($event)"/>
7969
+ [label]="'discussionComponent.attachFiles' | translate"
7970
+ [text]="true" />
7971
+ <input multiple type="file" (change)="onFilesSelected($event)" />
6767
7972
  </label>
6768
7973
  <p-button
6769
- [label]="'discussionComponent.send' | translate"
6770
7974
  icon="pi pi-send"
6771
- [loading]="submitting"
6772
7975
  [disabled]="submitting || !newComment.trim()"
6773
- (onClick)="createComment()"
6774
- />
7976
+ [label]="'discussionComponent.send' | translate"
7977
+ [loading]="submitting"
7978
+ (onClick)="createComment()" />
6775
7979
  </div>
6776
7980
  </div>
6777
7981
  </p-card>
@@ -6779,7 +7983,7 @@ class DiscussionComponent {
6779
7983
  @if (loading) {
6780
7984
  <div
6781
7985
  class="flex min-h-20 flex-col items-center justify-center gap-3 rounded-2xl border border-surface-200 px-4 py-5 text-surface-500 dark:border-surface-700">
6782
- <p-progressSpinner strokeWidth="4"/>
7986
+ <p-progressSpinner strokeWidth="4" />
6783
7987
  <span>{{ 'discussionComponent.loadingComments' | translate }}</span>
6784
7988
  </div>
6785
7989
  } @else if (comments.length === 0) {
@@ -6795,24 +7999,25 @@ class DiscussionComponent {
6795
7999
  <ng-template pTemplate="content">
6796
8000
  <div class="grid gap-4 md:grid-cols-[3.2rem_minmax(0,1fr)]">
6797
8001
  <div>
6798
- <p-avatar
6799
- [label]="getInitialsFromString(comment.authorDisplayName)"
6800
- shape="circle"
6801
- />
8002
+ <p-avatar shape="circle" [label]="getInitialsFromString(comment.authorDisplayName)" />
6802
8003
  </div>
6803
8004
 
6804
8005
  <div class="flex flex-col gap-2">
6805
8006
  <div class="flex flex-row gap-3 justify-between">
6806
8007
  <div class="flex flex-wrap items-center gap-2">
6807
8008
  <strong>{{ comment.authorDisplayName }}</strong>
6808
- <span
6809
- class="text-sm text-surface-500">{{ comment.createdAt | date: layoutService.dateTimeFormat() }}</span>
8009
+ <span class="text-sm text-surface-500">{{
8010
+ comment.createdAt | date: layoutService.dateTimeFormat()
8011
+ }}</span>
6810
8012
  </div>
6811
8013
 
6812
8014
  <div class="flex flex-wrap items-center justify-end gap-2">
6813
8015
  <div class="flex flex-wrap items-center">
6814
8016
  @if (comment.isEdited) {
6815
- <p-tag [value]="'discussionComponent.edited' | translate" icon="pi pi-pencil" severity="warn"/>
8017
+ <p-tag
8018
+ icon="pi pi-pencil"
8019
+ severity="warn"
8020
+ [value]="'discussionComponent.edited' | translate" />
6816
8021
  }
6817
8022
  </div>
6818
8023
  @if (comment.canEdit) {
@@ -6821,8 +8026,7 @@ class DiscussionComponent {
6821
8026
  severity="secondary"
6822
8027
  [rounded]="true"
6823
8028
  [text]="true"
6824
- (onClick)="startEdit(comment)"
6825
- />
8029
+ (onClick)="startEdit(comment)" />
6826
8030
  }
6827
8031
  @if (comment.historyCount > 0) {
6828
8032
  <p-button
@@ -6830,8 +8034,7 @@ class DiscussionComponent {
6830
8034
  severity="secondary"
6831
8035
  [rounded]="true"
6832
8036
  [text]="true"
6833
- (onClick)="toggleHistory(comment)"
6834
- />
8037
+ (onClick)="toggleHistory(comment)" />
6835
8038
  }
6836
8039
  @if (comment.canDelete) {
6837
8040
  <p-button
@@ -6839,47 +8042,42 @@ class DiscussionComponent {
6839
8042
  severity="danger"
6840
8043
  [rounded]="true"
6841
8044
  [text]="true"
6842
- (onClick)="deleteComment(comment)"
6843
- />
8045
+ (onClick)="deleteComment(comment)" />
6844
8046
  }
6845
8047
  </div>
6846
8048
  </div>
6847
8049
  @if (editingCommentId === comment.commentId) {
6848
8050
  <div class="flex flex-col gap-4">
6849
8051
  <textarea
6850
- pTextarea
6851
8052
  class="min-h-24 w-full"
6852
- [autoResize]="true"
8053
+ pTextarea
6853
8054
  rows="4"
6854
- [(ngModel)]="editContent"
8055
+ [autoResize]="true"
6855
8056
  [placeholder]="'discussionComponent.editCommentPlaceholder' | translate"
6856
- ></textarea>
8057
+ [(ngModel)]="editContent"></textarea>
6857
8058
  <div class="flex flex-col gap-2 sm:flex-row sm:justify-end">
6858
8059
  <div>
6859
8060
  <label class="file-trigger">
6860
8061
  <p-button
6861
- [label]="'discussionComponent.addAttachment' | translate"
6862
8062
  icon="pi pi-paperclip"
6863
- [text]="true"
6864
8063
  severity="secondary"
6865
- />
6866
- <input type="file" multiple (change)="onInlineFilesSelected(comment, $event)"/>
8064
+ [label]="'discussionComponent.addAttachment' | translate"
8065
+ [text]="true" />
8066
+ <input multiple type="file" (change)="onInlineFilesSelected(comment, $event)" />
6867
8067
  </label>
6868
8068
  </div>
6869
8069
 
6870
8070
  <p-button
6871
- [label]="'discussionComponent.save' | translate"
6872
8071
  icon="pi pi-check"
6873
8072
  [disabled]="!editContent.trim()"
6874
- (onClick)="saveEdit(comment)"
6875
- />
8073
+ [label]="'discussionComponent.save' | translate"
8074
+ (onClick)="saveEdit(comment)" />
6876
8075
  <p-button
6877
- [label]="'discussionComponent.cancel' | translate"
6878
8076
  icon="pi pi-times"
6879
8077
  severity="secondary"
8078
+ [label]="'discussionComponent.cancel' | translate"
6880
8079
  [text]="true"
6881
- (onClick)="cancelEdit()"
6882
- />
8080
+ (onClick)="cancelEdit()" />
6883
8081
  </div>
6884
8082
  </div>
6885
8083
  } @else {
@@ -6894,13 +8092,12 @@ class DiscussionComponent {
6894
8092
  <div class="inline-flex items-center gap-2">
6895
8093
  <i class="pi pi-file"></i>
6896
8094
  <button
6897
- type="button"
6898
8095
  class="break-all bg-transparent p-0 text-left font-semibold text-primary-600 transition hover:text-primary-500"
6899
- (click)="downloadAttachment(attachment)"
6900
- >
8096
+ type="button"
8097
+ (click)="downloadAttachment(attachment)">
6901
8098
  {{ attachment.fileName }}
6902
8099
  </button>
6903
- <p-tag [value]="formatBytes(attachment.fileSize)" severity="contrast"/>
8100
+ <p-tag severity="contrast" [value]="formatBytes(attachment.fileSize)" />
6904
8101
  </div>
6905
8102
 
6906
8103
  @if (comment.canEdit) {
@@ -6909,8 +8106,7 @@ class DiscussionComponent {
6909
8106
  severity="danger"
6910
8107
  [rounded]="true"
6911
8108
  [text]="true"
6912
- (onClick)="deleteAttachment(comment, attachment)"
6913
- />
8109
+ (onClick)="deleteAttachment(comment, attachment)" />
6914
8110
  }
6915
8111
  </div>
6916
8112
  }
@@ -6921,11 +8117,10 @@ class DiscussionComponent {
6921
8117
  <div class="flex flex-wrap items-center gap-2">
6922
8118
  @for (reaction of getReactions(comment); track reaction.emojiCode) {
6923
8119
  <p-button
6924
- [label]="reaction.emojiCode + ' ' + reaction.count"
6925
8120
  severity="secondary"
8121
+ [label]="reaction.emojiCode + ' ' + reaction.count"
6926
8122
  [outlined]="false"
6927
- (onClick)="toggleReaction(comment, reaction)"
6928
- />
8123
+ (onClick)="toggleReaction(comment, reaction)" />
6929
8124
  }
6930
8125
 
6931
8126
  @if (!hasUserReaction(comment)) {
@@ -6934,16 +8129,14 @@ class DiscussionComponent {
6934
8129
  severity="secondary"
6935
8130
  [rounded]="true"
6936
8131
  [text]="true"
6937
- (onClick)="reactionPopover.toggle($event)"
6938
- />
8132
+ (onClick)="reactionPopover.toggle($event)" />
6939
8133
  <p-popover #reactionPopover>
6940
8134
  <div class="emoji-popover">
6941
8135
  @for (emoji of emojiPalette; track emoji) {
6942
8136
  <button
6943
- type="button"
6944
8137
  class="emoji-option"
6945
- (click)="reactWithEmoji(comment, emoji, reactionPopover)"
6946
- >
8138
+ type="button"
8139
+ (click)="reactWithEmoji(comment, emoji, reactionPopover)">
6947
8140
  {{ emoji }}
6948
8141
  </button>
6949
8142
  }
@@ -6953,7 +8146,7 @@ class DiscussionComponent {
6953
8146
  </div>
6954
8147
 
6955
8148
  @if (historyByComment[comment.commentId]?.opened) {
6956
- <p-panel class="block" [toggleable]="true" [collapsed]="false">
8149
+ <p-panel class="block" [collapsed]="false" [toggleable]="true">
6957
8150
  <ng-template pTemplate="header">
6958
8151
  <div class="inline-flex items-center gap-2 text-sm font-medium">
6959
8152
  <i class="pi pi-history"></i>
@@ -6963,12 +8156,15 @@ class DiscussionComponent {
6963
8156
 
6964
8157
  @if (historyByComment[comment.commentId]?.loading) {
6965
8158
  <div class="flex items-center gap-3 text-surface-500">
6966
- <p-progressSpinner strokeWidth="4"/>
8159
+ <p-progressSpinner strokeWidth="4" />
6967
8160
  <span>{{ 'discussionComponent.loadingHistory' | translate }}</span>
6968
8161
  </div>
6969
8162
  } @else {
6970
8163
  <div class="flex flex-col gap-4">
6971
- @for (item of historyByComment[comment.commentId]?.items ?? []; track item.commentEditHistoryId) {
8164
+ @for (
8165
+ item of historyByComment[comment.commentId]?.items ?? [];
8166
+ track item.commentEditHistoryId
8167
+ ) {
6972
8168
  <div class="rounded-2xl border border-surface-200 px-4 py-4 dark:border-surface-700">
6973
8169
  <div class="mb-3 inline-flex items-center gap-2 text-sm font-semibold">
6974
8170
  <i class="pi pi-clock"></i>
@@ -6976,18 +8172,16 @@ class DiscussionComponent {
6976
8172
  </div>
6977
8173
  <div class="grid gap-4 lg:grid-cols-2">
6978
8174
  <div class="flex flex-col gap-2">
6979
- <p-tag [value]="'discussionComponent.before' | translate" severity="secondary"/>
8175
+ <p-tag severity="secondary" [value]="'discussionComponent.before' | translate" />
6980
8176
  <div
6981
8177
  class="history-markdown rounded-2xl bg-surface-50 px-4 py-3 leading-7 dark:bg-surface-900/40"
6982
- [innerHTML]="renderMarkdown(item.oldContent)"
6983
- ></div>
8178
+ [innerHTML]="renderMarkdown(item.oldContent)"></div>
6984
8179
  </div>
6985
8180
  <div class="flex flex-col gap-2">
6986
- <p-tag [value]="'discussionComponent.after' | translate" severity="success"/>
8181
+ <p-tag severity="success" [value]="'discussionComponent.after' | translate" />
6987
8182
  <div
6988
8183
  class="history-markdown rounded-2xl bg-surface-50 px-4 py-3 leading-7 dark:bg-surface-900/40"
6989
- [innerHTML]="renderMarkdown(item.newContent)"
6990
- ></div>
8184
+ [innerHTML]="renderMarkdown(item.newContent)"></div>
6991
8185
  </div>
6992
8186
  </div>
6993
8187
  </div>
@@ -7004,7 +8198,7 @@ class DiscussionComponent {
7004
8198
  </div>
7005
8199
  }
7006
8200
  </section>
7007
- `, 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$1.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "directive", type: i2$2.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: i4.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: i1$3.DatePipe, name: "date" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8201
+ `, 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$1.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$2.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: i4.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: i1$3.DatePipe, name: "date" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
7008
8202
  }
7009
8203
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: DiscussionComponent, decorators: [{
7010
8204
  type: Component,
@@ -7024,27 +8218,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7024
8218
  DatePipe,
7025
8219
  TranslatePipe
7026
8220
  ], template: `
7027
- <p-confirmDialog/>
8221
+ <p-confirmDialog />
7028
8222
 
7029
8223
  <section class="flex flex-col gap-4">
7030
-
7031
8224
  <p-card class="block">
7032
8225
  <div class="flex flex-col gap-4">
7033
8226
  @if (!previewMode) {
7034
8227
  <textarea
7035
- pTextarea
7036
8228
  class="min-h-28 w-full"
7037
- [autoResize]="true"
8229
+ pTextarea
7038
8230
  rows="5"
7039
- [(ngModel)]="newComment"
7040
- (ngModelChange)="onComposerInput()"
8231
+ [autoResize]="true"
7041
8232
  [placeholder]="'discussionComponent.writeCommentPlaceholder' | translate"
7042
- ></textarea>
8233
+ [(ngModel)]="newComment"
8234
+ (ngModelChange)="onComposerInput()"></textarea>
7043
8235
  } @else {
7044
8236
  <div
7045
8237
  class="markdown-preview min-h-28 rounded-2xl border border-surface-200 bg-surface-50 px-4 py-3 leading-7 dark:border-surface-700 dark:bg-surface-900/40"
7046
- [innerHTML]="renderMarkdown(newComment)"
7047
- ></div>
8238
+ [innerHTML]="renderMarkdown(newComment)"></div>
7048
8239
  }
7049
8240
 
7050
8241
  @if (mentionSuggestions.length > 0) {
@@ -7059,10 +8250,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7059
8250
  <div class="flex flex-col gap-3">
7060
8251
  @for (candidate of mentionSuggestions; track candidate.userId) {
7061
8252
  <button
7062
- type="button"
7063
8253
  class="flex w-full items-center justify-between gap-4 rounded-xl border border-transparent px-3 py-3 text-left transition hover:border-primary-200 hover:bg-primary-50 dark:hover:border-primary-800 dark:hover:bg-primary-950/30"
7064
- (click)="insertMention(candidate)"
7065
- >
8254
+ type="button"
8255
+ (click)="insertMention(candidate)">
7066
8256
  <div class="min-w-0">
7067
8257
  <strong>{{ candidate.displayName }}</strong>
7068
8258
  <small class="mt-1 block text-surface-500">{{ candidate.email }}</small>
@@ -7082,15 +8272,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7082
8272
  <div class="inline-flex items-center gap-2">
7083
8273
  <i class="pi pi-file"></i>
7084
8274
  <span class="break-all">{{ file.name }}</span>
7085
- <p-tag [value]="formatBytes(file.size)" severity="contrast"/>
8275
+ <p-tag severity="contrast" [value]="formatBytes(file.size)" />
7086
8276
  </div>
7087
8277
  <p-button
7088
8278
  icon="pi pi-times"
7089
8279
  severity="danger"
7090
8280
  [rounded]="true"
7091
8281
  [text]="true"
7092
- (onClick)="removePendingFile(file)"
7093
- />
8282
+ (onClick)="removePendingFile(file)" />
7094
8283
  </div>
7095
8284
  }
7096
8285
  </div>
@@ -7098,33 +8287,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7098
8287
 
7099
8288
  <div class="flex flex-col gap-2 sm:flex-row sm:justify-end">
7100
8289
  <p-button
8290
+ severity="secondary"
8291
+ [icon]="previewMode ? 'pi pi-pencil' : 'pi pi-eye'"
7101
8292
  [label]="
7102
- previewMode
7103
- ? ('discussionComponent.edit' | translate)
7104
- : ('discussionComponent.preview' | translate)
8293
+ previewMode ? ('discussionComponent.edit' | translate) : ('discussionComponent.preview' | translate)
7105
8294
  "
7106
- [icon]="previewMode ? 'pi pi-pencil' : 'pi pi-eye'"
7107
- severity="secondary"
7108
8295
  [text]="true"
7109
- (onClick)="previewMode = !previewMode"
7110
- />
8296
+ (onClick)="previewMode = !previewMode" />
7111
8297
 
7112
8298
  <label class="file-trigger">
7113
8299
  <p-button
7114
- [label]="'discussionComponent.attachFiles' | translate"
7115
8300
  icon="pi pi-paperclip"
7116
8301
  severity="secondary"
7117
- [text]="true"
7118
- />
7119
- <input type="file" multiple (change)="onFilesSelected($event)"/>
8302
+ [label]="'discussionComponent.attachFiles' | translate"
8303
+ [text]="true" />
8304
+ <input multiple type="file" (change)="onFilesSelected($event)" />
7120
8305
  </label>
7121
8306
  <p-button
7122
- [label]="'discussionComponent.send' | translate"
7123
8307
  icon="pi pi-send"
7124
- [loading]="submitting"
7125
8308
  [disabled]="submitting || !newComment.trim()"
7126
- (onClick)="createComment()"
7127
- />
8309
+ [label]="'discussionComponent.send' | translate"
8310
+ [loading]="submitting"
8311
+ (onClick)="createComment()" />
7128
8312
  </div>
7129
8313
  </div>
7130
8314
  </p-card>
@@ -7132,7 +8316,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7132
8316
  @if (loading) {
7133
8317
  <div
7134
8318
  class="flex min-h-20 flex-col items-center justify-center gap-3 rounded-2xl border border-surface-200 px-4 py-5 text-surface-500 dark:border-surface-700">
7135
- <p-progressSpinner strokeWidth="4"/>
8319
+ <p-progressSpinner strokeWidth="4" />
7136
8320
  <span>{{ 'discussionComponent.loadingComments' | translate }}</span>
7137
8321
  </div>
7138
8322
  } @else if (comments.length === 0) {
@@ -7148,24 +8332,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7148
8332
  <ng-template pTemplate="content">
7149
8333
  <div class="grid gap-4 md:grid-cols-[3.2rem_minmax(0,1fr)]">
7150
8334
  <div>
7151
- <p-avatar
7152
- [label]="getInitialsFromString(comment.authorDisplayName)"
7153
- shape="circle"
7154
- />
8335
+ <p-avatar shape="circle" [label]="getInitialsFromString(comment.authorDisplayName)" />
7155
8336
  </div>
7156
8337
 
7157
8338
  <div class="flex flex-col gap-2">
7158
8339
  <div class="flex flex-row gap-3 justify-between">
7159
8340
  <div class="flex flex-wrap items-center gap-2">
7160
8341
  <strong>{{ comment.authorDisplayName }}</strong>
7161
- <span
7162
- class="text-sm text-surface-500">{{ comment.createdAt | date: layoutService.dateTimeFormat() }}</span>
8342
+ <span class="text-sm text-surface-500">{{
8343
+ comment.createdAt | date: layoutService.dateTimeFormat()
8344
+ }}</span>
7163
8345
  </div>
7164
8346
 
7165
8347
  <div class="flex flex-wrap items-center justify-end gap-2">
7166
8348
  <div class="flex flex-wrap items-center">
7167
8349
  @if (comment.isEdited) {
7168
- <p-tag [value]="'discussionComponent.edited' | translate" icon="pi pi-pencil" severity="warn"/>
8350
+ <p-tag
8351
+ icon="pi pi-pencil"
8352
+ severity="warn"
8353
+ [value]="'discussionComponent.edited' | translate" />
7169
8354
  }
7170
8355
  </div>
7171
8356
  @if (comment.canEdit) {
@@ -7174,8 +8359,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7174
8359
  severity="secondary"
7175
8360
  [rounded]="true"
7176
8361
  [text]="true"
7177
- (onClick)="startEdit(comment)"
7178
- />
8362
+ (onClick)="startEdit(comment)" />
7179
8363
  }
7180
8364
  @if (comment.historyCount > 0) {
7181
8365
  <p-button
@@ -7183,8 +8367,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7183
8367
  severity="secondary"
7184
8368
  [rounded]="true"
7185
8369
  [text]="true"
7186
- (onClick)="toggleHistory(comment)"
7187
- />
8370
+ (onClick)="toggleHistory(comment)" />
7188
8371
  }
7189
8372
  @if (comment.canDelete) {
7190
8373
  <p-button
@@ -7192,47 +8375,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7192
8375
  severity="danger"
7193
8376
  [rounded]="true"
7194
8377
  [text]="true"
7195
- (onClick)="deleteComment(comment)"
7196
- />
8378
+ (onClick)="deleteComment(comment)" />
7197
8379
  }
7198
8380
  </div>
7199
8381
  </div>
7200
8382
  @if (editingCommentId === comment.commentId) {
7201
8383
  <div class="flex flex-col gap-4">
7202
8384
  <textarea
7203
- pTextarea
7204
8385
  class="min-h-24 w-full"
7205
- [autoResize]="true"
8386
+ pTextarea
7206
8387
  rows="4"
7207
- [(ngModel)]="editContent"
8388
+ [autoResize]="true"
7208
8389
  [placeholder]="'discussionComponent.editCommentPlaceholder' | translate"
7209
- ></textarea>
8390
+ [(ngModel)]="editContent"></textarea>
7210
8391
  <div class="flex flex-col gap-2 sm:flex-row sm:justify-end">
7211
8392
  <div>
7212
8393
  <label class="file-trigger">
7213
8394
  <p-button
7214
- [label]="'discussionComponent.addAttachment' | translate"
7215
8395
  icon="pi pi-paperclip"
7216
- [text]="true"
7217
8396
  severity="secondary"
7218
- />
7219
- <input type="file" multiple (change)="onInlineFilesSelected(comment, $event)"/>
8397
+ [label]="'discussionComponent.addAttachment' | translate"
8398
+ [text]="true" />
8399
+ <input multiple type="file" (change)="onInlineFilesSelected(comment, $event)" />
7220
8400
  </label>
7221
8401
  </div>
7222
8402
 
7223
8403
  <p-button
7224
- [label]="'discussionComponent.save' | translate"
7225
8404
  icon="pi pi-check"
7226
8405
  [disabled]="!editContent.trim()"
7227
- (onClick)="saveEdit(comment)"
7228
- />
8406
+ [label]="'discussionComponent.save' | translate"
8407
+ (onClick)="saveEdit(comment)" />
7229
8408
  <p-button
7230
- [label]="'discussionComponent.cancel' | translate"
7231
8409
  icon="pi pi-times"
7232
8410
  severity="secondary"
8411
+ [label]="'discussionComponent.cancel' | translate"
7233
8412
  [text]="true"
7234
- (onClick)="cancelEdit()"
7235
- />
8413
+ (onClick)="cancelEdit()" />
7236
8414
  </div>
7237
8415
  </div>
7238
8416
  } @else {
@@ -7247,13 +8425,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7247
8425
  <div class="inline-flex items-center gap-2">
7248
8426
  <i class="pi pi-file"></i>
7249
8427
  <button
7250
- type="button"
7251
8428
  class="break-all bg-transparent p-0 text-left font-semibold text-primary-600 transition hover:text-primary-500"
7252
- (click)="downloadAttachment(attachment)"
7253
- >
8429
+ type="button"
8430
+ (click)="downloadAttachment(attachment)">
7254
8431
  {{ attachment.fileName }}
7255
8432
  </button>
7256
- <p-tag [value]="formatBytes(attachment.fileSize)" severity="contrast"/>
8433
+ <p-tag severity="contrast" [value]="formatBytes(attachment.fileSize)" />
7257
8434
  </div>
7258
8435
 
7259
8436
  @if (comment.canEdit) {
@@ -7262,8 +8439,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7262
8439
  severity="danger"
7263
8440
  [rounded]="true"
7264
8441
  [text]="true"
7265
- (onClick)="deleteAttachment(comment, attachment)"
7266
- />
8442
+ (onClick)="deleteAttachment(comment, attachment)" />
7267
8443
  }
7268
8444
  </div>
7269
8445
  }
@@ -7274,11 +8450,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7274
8450
  <div class="flex flex-wrap items-center gap-2">
7275
8451
  @for (reaction of getReactions(comment); track reaction.emojiCode) {
7276
8452
  <p-button
7277
- [label]="reaction.emojiCode + ' ' + reaction.count"
7278
8453
  severity="secondary"
8454
+ [label]="reaction.emojiCode + ' ' + reaction.count"
7279
8455
  [outlined]="false"
7280
- (onClick)="toggleReaction(comment, reaction)"
7281
- />
8456
+ (onClick)="toggleReaction(comment, reaction)" />
7282
8457
  }
7283
8458
 
7284
8459
  @if (!hasUserReaction(comment)) {
@@ -7287,16 +8462,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7287
8462
  severity="secondary"
7288
8463
  [rounded]="true"
7289
8464
  [text]="true"
7290
- (onClick)="reactionPopover.toggle($event)"
7291
- />
8465
+ (onClick)="reactionPopover.toggle($event)" />
7292
8466
  <p-popover #reactionPopover>
7293
8467
  <div class="emoji-popover">
7294
8468
  @for (emoji of emojiPalette; track emoji) {
7295
8469
  <button
7296
- type="button"
7297
8470
  class="emoji-option"
7298
- (click)="reactWithEmoji(comment, emoji, reactionPopover)"
7299
- >
8471
+ type="button"
8472
+ (click)="reactWithEmoji(comment, emoji, reactionPopover)">
7300
8473
  {{ emoji }}
7301
8474
  </button>
7302
8475
  }
@@ -7306,7 +8479,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7306
8479
  </div>
7307
8480
 
7308
8481
  @if (historyByComment[comment.commentId]?.opened) {
7309
- <p-panel class="block" [toggleable]="true" [collapsed]="false">
8482
+ <p-panel class="block" [collapsed]="false" [toggleable]="true">
7310
8483
  <ng-template pTemplate="header">
7311
8484
  <div class="inline-flex items-center gap-2 text-sm font-medium">
7312
8485
  <i class="pi pi-history"></i>
@@ -7316,12 +8489,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7316
8489
 
7317
8490
  @if (historyByComment[comment.commentId]?.loading) {
7318
8491
  <div class="flex items-center gap-3 text-surface-500">
7319
- <p-progressSpinner strokeWidth="4"/>
8492
+ <p-progressSpinner strokeWidth="4" />
7320
8493
  <span>{{ 'discussionComponent.loadingHistory' | translate }}</span>
7321
8494
  </div>
7322
8495
  } @else {
7323
8496
  <div class="flex flex-col gap-4">
7324
- @for (item of historyByComment[comment.commentId]?.items ?? []; track item.commentEditHistoryId) {
8497
+ @for (
8498
+ item of historyByComment[comment.commentId]?.items ?? [];
8499
+ track item.commentEditHistoryId
8500
+ ) {
7325
8501
  <div class="rounded-2xl border border-surface-200 px-4 py-4 dark:border-surface-700">
7326
8502
  <div class="mb-3 inline-flex items-center gap-2 text-sm font-semibold">
7327
8503
  <i class="pi pi-clock"></i>
@@ -7329,18 +8505,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7329
8505
  </div>
7330
8506
  <div class="grid gap-4 lg:grid-cols-2">
7331
8507
  <div class="flex flex-col gap-2">
7332
- <p-tag [value]="'discussionComponent.before' | translate" severity="secondary"/>
8508
+ <p-tag severity="secondary" [value]="'discussionComponent.before' | translate" />
7333
8509
  <div
7334
8510
  class="history-markdown rounded-2xl bg-surface-50 px-4 py-3 leading-7 dark:bg-surface-900/40"
7335
- [innerHTML]="renderMarkdown(item.oldContent)"
7336
- ></div>
8511
+ [innerHTML]="renderMarkdown(item.oldContent)"></div>
7337
8512
  </div>
7338
8513
  <div class="flex flex-col gap-2">
7339
- <p-tag [value]="'discussionComponent.after' | translate" severity="success"/>
8514
+ <p-tag severity="success" [value]="'discussionComponent.after' | translate" />
7340
8515
  <div
7341
8516
  class="history-markdown rounded-2xl bg-surface-50 px-4 py-3 leading-7 dark:bg-surface-900/40"
7342
- [innerHTML]="renderMarkdown(item.newContent)"
7343
- ></div>
8517
+ [innerHTML]="renderMarkdown(item.newContent)"></div>
7344
8518
  </div>
7345
8519
  </div>
7346
8520
  </div>
@@ -7366,13 +8540,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7366
8540
  args: [{ required: true }]
7367
8541
  }] } });
7368
8542
 
8543
+ var en = {
8544
+ "iframe-module": {
8545
+ iframeModule: {
8546
+ urlPlaceholder: "Site URL",
8547
+ settingSaveButtonLabel: "Save",
8548
+ emptyUrlMessage: "Site URL is not specified.",
8549
+ siteLoadingMessage: "Failed to load the site."
8550
+ }
8551
+ }
8552
+ };
8553
+
8554
+ var ru = {
8555
+ "iframe-module": {
8556
+ iframeModule: {
8557
+ urlPlaceholder: "URL сайта",
8558
+ settingSaveButtonLabel: "Сохранить",
8559
+ emptyUrlMessage: "URL сайта не указан.",
8560
+ siteLoadingMessage: "Не удалось загрузить сайт."
8561
+ }
8562
+ }
8563
+ };
8564
+
7369
8565
  class IframeModuleComponent extends BaseModuleComponent {
8566
+ static { this.translations = { en, ru }; }
7370
8567
  constructor() {
7371
8568
  super();
7372
8569
  this.renderer = inject(Renderer2);
7373
8570
  this.translate = inject(TranslateService);
7374
8571
  this.iframeUrl = null;
7375
- this.l10nService.loadComponentTranslations('iframe-module');
7376
8572
  }
7377
8573
  set iframeElement(element) {
7378
8574
  this.iframe = element;
@@ -7445,7 +8641,7 @@ class IframeModuleComponent extends BaseModuleComponent {
7445
8641
  placeholder="{{ 'iframe-module.iframeModule.urlPlaceholder' | translate }}"
7446
8642
  qa-id="iframe-module-settings-site-url-input"
7447
8643
  type="text"
7448
- [(ngModel)]="settings.url"/>
8644
+ [(ngModel)]="settings.url" />
7449
8645
  </div>
7450
8646
  <div class="flex justify-end">
7451
8647
  <p-button
@@ -7459,7 +8655,7 @@ class IframeModuleComponent extends BaseModuleComponent {
7459
8655
  </div>
7460
8656
  </div>
7461
8657
  } @else if (isSecurity) {
7462
- <security [controller]="controller" [id]="id"/>
8658
+ <security [controller]="controller" [id]="id" />
7463
8659
  }
7464
8660
  `, isInline: true, dependencies: [{ kind: "component", type: SecurityComponent, selector: "security", inputs: ["id", "controller"] }, { kind: "directive", type: 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: "component", type: 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: "pipe", type: TranslatePipe, name: "translate" }] }); }
7465
8661
  }
@@ -7489,7 +8685,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7489
8685
  placeholder="{{ 'iframe-module.iframeModule.urlPlaceholder' | translate }}"
7490
8686
  qa-id="iframe-module-settings-site-url-input"
7491
8687
  type="text"
7492
- [(ngModel)]="settings.url"/>
8688
+ [(ngModel)]="settings.url" />
7493
8689
  </div>
7494
8690
  <div class="flex justify-end">
7495
8691
  <p-button
@@ -7503,7 +8699,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7503
8699
  </div>
7504
8700
  </div>
7505
8701
  } @else if (isSecurity) {
7506
- <security [controller]="controller" [id]="id"/>
8702
+ <security [controller]="controller" [id]="id" />
7507
8703
  }
7508
8704
  `
7509
8705
  }]
@@ -7512,6 +8708,563 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7512
8708
  args: ['iframe']
7513
8709
  }] } });
7514
8710
 
8711
+ const OIP_EXTENSION_EVENTS = {
8712
+ contextChange: 'oip:context-change',
8713
+ titleChange: 'oip:title-change',
8714
+ settingsChange: 'oip:settings-change',
8715
+ notify: 'oip:notify',
8716
+ navigate: 'oip:navigate',
8717
+ error: 'oip:error'
8718
+ };
8719
+ function emitOipTitleChange(element, title) {
8720
+ element.dispatchEvent(new CustomEvent(OIP_EXTENSION_EVENTS.titleChange, { detail: title, bubbles: true }));
8721
+ }
8722
+ function emitOipSettingsChange(element, settings) {
8723
+ element.dispatchEvent(new CustomEvent(OIP_EXTENSION_EVENTS.settingsChange, { detail: settings, bubbles: true }));
8724
+ }
8725
+ function emitOipNotify(element, notification) {
8726
+ element.dispatchEvent(new CustomEvent(OIP_EXTENSION_EVENTS.notify, { detail: notification, bubbles: true }));
8727
+ }
8728
+ function emitOipNavigate(element, navigation) {
8729
+ element.dispatchEvent(new CustomEvent(OIP_EXTENSION_EVENTS.navigate, { detail: navigation, bubbles: true }));
8730
+ }
8731
+ function emitOipError(element, error) {
8732
+ element.dispatchEvent(new CustomEvent(OIP_EXTENSION_EVENTS.error, { detail: error, bubbles: true }));
8733
+ }
8734
+ function emitOipContextChange(element, context) {
8735
+ element.dispatchEvent(new CustomEvent(OIP_EXTENSION_EVENTS.contextChange, { detail: context }));
8736
+ }
8737
+
8738
+ class ExtensionLoaderService {
8739
+ constructor() {
8740
+ this.loadedScripts = new Map();
8741
+ }
8742
+ loadScript(scriptUrl) {
8743
+ const existing = this.loadedScripts.get(scriptUrl);
8744
+ if (existing) {
8745
+ return existing;
8746
+ }
8747
+ const loadPromise = new Promise((resolve, reject) => {
8748
+ const script = document.createElement('script');
8749
+ script.type = 'module';
8750
+ script.src = scriptUrl;
8751
+ script.async = true;
8752
+ script.onload = () => resolve();
8753
+ script.onerror = () => {
8754
+ this.loadedScripts.delete(scriptUrl);
8755
+ reject(new Error(`Extension script could not be loaded: ${scriptUrl}`));
8756
+ };
8757
+ document.head.appendChild(script);
8758
+ });
8759
+ this.loadedScripts.set(scriptUrl, loadPromise);
8760
+ return loadPromise;
8761
+ }
8762
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ExtensionLoaderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
8763
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ExtensionLoaderService, providedIn: 'root' }); }
8764
+ }
8765
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ExtensionLoaderService, decorators: [{
8766
+ type: Injectable,
8767
+ args: [{ providedIn: 'root' }]
8768
+ }] });
8769
+
8770
+ class CustomElementExtensionModuleHostComponent extends BaseModuleComponent {
8771
+ set extensionContainer(element) {
8772
+ this.container = element;
8773
+ this.queueRenderExtension();
8774
+ }
8775
+ get showContent() {
8776
+ return this.isActiveTab('content');
8777
+ }
8778
+ get showSettings() {
8779
+ return this.isActiveTab('settings');
8780
+ }
8781
+ get showSecurity() {
8782
+ return this.isActiveTab('security');
8783
+ }
8784
+ get extensionKey() {
8785
+ return this.route.snapshot.paramMap.get('extensionKey') ?? '';
8786
+ }
8787
+ constructor() {
8788
+ super();
8789
+ this.extensionLoader = inject(ExtensionLoaderService);
8790
+ this.router = inject(Router);
8791
+ this.hostChangeDetectorRef = inject(ChangeDetectorRef);
8792
+ this.extensionModulesApi = inject(ExtensionModulesApi);
8793
+ this.removeListeners = [];
8794
+ this.renderQueued = false;
8795
+ this.activeTabSyncQueued = false;
8796
+ this.activeTabId = this.getCurrentActiveTabId();
8797
+ this.destroyed = false;
8798
+ this.loadError = null;
8799
+ this.subscriptions.push(this.topBarService.activeId$.subscribe(() => {
8800
+ this.queueActiveTabSync();
8801
+ }));
8802
+ }
8803
+ async onModuleInstanceChange() {
8804
+ await this.loadExtensionMetadata();
8805
+ await this.renderExtension();
8806
+ }
8807
+ onSecurityRightsChange() {
8808
+ this.updateExtensionContext();
8809
+ }
8810
+ ngOnDestroy() {
8811
+ this.destroyed = true;
8812
+ this.destroyExtensionElement();
8813
+ super.ngOnDestroy();
8814
+ }
8815
+ async reloadExtension() {
8816
+ this.loadError = null;
8817
+ this.extensionMetadata = undefined;
8818
+ await this.loadExtensionMetadata();
8819
+ await this.renderExtension();
8820
+ }
8821
+ async loadExtensionMetadata() {
8822
+ if (this.extensionMetadata) {
8823
+ return;
8824
+ }
8825
+ if (!this.extensionKey) {
8826
+ this.loadError = 'Extension key is missing.';
8827
+ return;
8828
+ }
8829
+ this.extensionMetadata = (await this.extensionModulesApi.getExtensionModuleByKey({
8830
+ extensionKey: this.extensionKey
8831
+ }));
8832
+ await this.loadExtensionTranslations(this.extensionMetadata);
8833
+ }
8834
+ async renderExtension() {
8835
+ if (this.destroyed || !this.container || !this.extensionMetadata || !this.showContent) {
8836
+ return;
8837
+ }
8838
+ try {
8839
+ this.loadError = null;
8840
+ this.destroyExtensionElement();
8841
+ await this.renderCustomElementExtension(this.extensionMetadata);
8842
+ }
8843
+ catch (error) {
8844
+ this.loadError = error instanceof Error ? error.message : 'Extension could not be loaded.';
8845
+ this.msgService.error(error);
8846
+ }
8847
+ }
8848
+ queueRenderExtension() {
8849
+ if (this.renderQueued) {
8850
+ return;
8851
+ }
8852
+ this.renderQueued = true;
8853
+ queueMicrotask(() => {
8854
+ this.renderQueued = false;
8855
+ void this.renderExtension();
8856
+ });
8857
+ }
8858
+ isActiveTab(id) {
8859
+ return this.activeTabId === id;
8860
+ }
8861
+ queueActiveTabSync() {
8862
+ const nextActiveTabId = this.getCurrentActiveTabId();
8863
+ if (this.activeTabId === nextActiveTabId || this.activeTabSyncQueued) {
8864
+ return;
8865
+ }
8866
+ this.activeTabSyncQueued = true;
8867
+ queueMicrotask(() => {
8868
+ this.activeTabSyncQueued = false;
8869
+ if (this.destroyed) {
8870
+ return;
8871
+ }
8872
+ this.activeTabId = this.getCurrentActiveTabId();
8873
+ this.hostChangeDetectorRef.detectChanges();
8874
+ if (this.showContent) {
8875
+ this.queueRenderExtension();
8876
+ }
8877
+ else {
8878
+ this.destroyExtensionElement();
8879
+ }
8880
+ });
8881
+ }
8882
+ getCurrentActiveTabId() {
8883
+ return this.topBarService.activeTopBarItem?.id ?? 'content';
8884
+ }
8885
+ updateExtensionContext() {
8886
+ if (!this.extensionElement || !this.extensionMetadata?.extensionKey) {
8887
+ return;
8888
+ }
8889
+ const context = {
8890
+ moduleInstanceId: this.id,
8891
+ extensionKey: this.extensionMetadata.extensionKey,
8892
+ apiBasePath: this.buildUrl(`api/extensions/${this.extensionMetadata.extensionKey}`),
8893
+ settings: this.settings,
8894
+ locale: this.layoutService.language(),
8895
+ theme: this.layoutService.layoutConfig(),
8896
+ user: this.securityService.getCurrentUser(),
8897
+ activeTabId: this.topBarService.activeId ?? 'content',
8898
+ permissions: {
8899
+ canRead: this.canRead,
8900
+ canEdit: this.canEdit,
8901
+ canDelete: this.canDelete
8902
+ }
8903
+ };
8904
+ Object.assign(this.extensionElement, { oipContext: context });
8905
+ emitOipContextChange(this.extensionElement, context);
8906
+ }
8907
+ attachListeners(element) {
8908
+ this.addListener(element, OIP_EXTENSION_EVENTS.titleChange, (event) => {
8909
+ const title = event.detail;
8910
+ if (title) {
8911
+ this.appTitleService.setTitle(title);
8912
+ }
8913
+ });
8914
+ this.addListener(element, OIP_EXTENSION_EVENTS.settingsChange, (event) => {
8915
+ this.settings = event.detail;
8916
+ void this.saveSettings(this.settings);
8917
+ });
8918
+ this.addListener(element, OIP_EXTENSION_EVENTS.notify, (event) => {
8919
+ const detail = event.detail;
8920
+ const severity = detail?.severity ?? 'info';
8921
+ const message = detail?.detail ?? detail?.summary ?? '';
8922
+ switch (severity) {
8923
+ case 'success':
8924
+ this.msgService.success(message, detail?.summary);
8925
+ break;
8926
+ case 'warn':
8927
+ this.msgService.warn(message, detail?.summary);
8928
+ break;
8929
+ case 'error':
8930
+ this.msgService.error(message, detail?.summary);
8931
+ break;
8932
+ default:
8933
+ this.msgService.info(message, detail?.summary);
8934
+ }
8935
+ });
8936
+ this.addListener(element, OIP_EXTENSION_EVENTS.navigate, (event) => {
8937
+ const detail = event.detail;
8938
+ if (detail?.commands) {
8939
+ void this.router.navigate(detail.commands);
8940
+ }
8941
+ else if (detail?.url) {
8942
+ void this.router.navigateByUrl(detail.url);
8943
+ }
8944
+ });
8945
+ this.addListener(element, OIP_EXTENSION_EVENTS.error, (event) => {
8946
+ this.msgService.error(event.detail);
8947
+ });
8948
+ }
8949
+ buildUrl(path) {
8950
+ const baseUrl = document.getElementsByTagName('base')[0]?.href ?? `${window.location.origin}/`;
8951
+ return `${baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`}${path}`;
8952
+ }
8953
+ async loadExtensionTranslations(metadata) {
8954
+ const extensionKey = metadata.extensionKey ?? this.extensionKey;
8955
+ const apiBaseUrl = metadata.apiBaseUrl;
8956
+ if (!extensionKey || !apiBaseUrl) {
8957
+ return;
8958
+ }
8959
+ const lang = this.translateService.currentLang || this.layoutService.language() || 'en';
8960
+ const url = this.buildExtensionI18nUrl(apiBaseUrl, extensionKey, lang);
8961
+ try {
8962
+ await firstValueFrom(this.l10nService.loadTranslationsFromUrl(extensionKey, url, lang));
8963
+ }
8964
+ catch (error) {
8965
+ console.error(`No translations found for extension ${extensionKey}.${lang}.json`, error);
8966
+ }
8967
+ }
8968
+ buildExtensionI18nUrl(apiBaseUrl, extensionKey, lang) {
8969
+ return `${apiBaseUrl.replace(/\/+$/, '')}/assets/i18n/${encodeURIComponent(extensionKey)}.${encodeURIComponent(lang)}.json`;
8970
+ }
8971
+ addListener(element, eventName, listener) {
8972
+ element.addEventListener(eventName, listener);
8973
+ this.removeListeners.push(() => element.removeEventListener(eventName, listener));
8974
+ }
8975
+ destroyExtensionElement() {
8976
+ this.removeListeners.forEach((remove) => remove());
8977
+ this.removeListeners = [];
8978
+ this.extensionElement?.remove();
8979
+ this.extensionElement = undefined;
8980
+ }
8981
+ async renderCustomElementExtension(metadata) {
8982
+ const { elementName, scriptUrl } = metadata;
8983
+ if (!this.container || !elementName || !scriptUrl) {
8984
+ this.loadError = 'Custom Element extension metadata is incomplete.';
8985
+ return;
8986
+ }
8987
+ await this.extensionLoader.loadScript(scriptUrl);
8988
+ await customElements.whenDefined(elementName);
8989
+ const element = document.createElement(elementName);
8990
+ this.extensionElement = element;
8991
+ this.attachListeners(element);
8992
+ this.container.nativeElement.appendChild(element);
8993
+ this.updateExtensionContext();
8994
+ }
8995
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: CustomElementExtensionModuleHostComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8996
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.24", type: CustomElementExtensionModuleHostComponent, isStandalone: true, selector: "ng-component", providers: [ExtensionModulesApi], viewQueries: [{ propertyName: "extensionContainer", first: true, predicate: ["extensionContainer"], descendants: true }], usesInheritance: true, ngImport: i0, template: `
8997
+ @if (showContent) {
8998
+ <div class="min-h-[calc(100vh-10rem)] w-full">
8999
+ @if (loadError) {
9000
+ <div class="card flex flex-col gap-4">
9001
+ <div class="font-semibold text-xl">{{ loadError }}</div>
9002
+ <p-button icon="pi pi-refresh" label="Retry" (onClick)="reloadExtension()"></p-button>
9003
+ </div>
9004
+ } @else {
9005
+ <div #extensionContainer class="w-full"></div>
9006
+ }
9007
+ </div>
9008
+ } @else if (showSettings) {
9009
+ <div class="flex flex-col md:flex-row gap-8">
9010
+ <div class="md:w-1/2">
9011
+ <div class="card flex flex-col gap-4">
9012
+ <div class="font-semibold text-xl">{{ 'baseComponent.settings' | translate }}</div>
9013
+ <p-button icon="pi pi-save" label="Save" (onClick)="saveSettings(settings)"></p-button>
9014
+ </div>
9015
+ </div>
9016
+ </div>
9017
+ } @else if (showSecurity) {
9018
+ <security [controller]="controller" [id]="id" />
9019
+ }
9020
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: SecurityComponent, selector: "security", inputs: ["id", "controller"] }, { kind: "component", type: 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: "pipe", type: TranslatePipe, name: "translate" }] }); }
9021
+ }
9022
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: CustomElementExtensionModuleHostComponent, decorators: [{
9023
+ type: Component,
9024
+ args: [{
9025
+ standalone: true,
9026
+ imports: [CommonModule, SecurityComponent, TranslatePipe, Button],
9027
+ providers: [ExtensionModulesApi],
9028
+ template: `
9029
+ @if (showContent) {
9030
+ <div class="min-h-[calc(100vh-10rem)] w-full">
9031
+ @if (loadError) {
9032
+ <div class="card flex flex-col gap-4">
9033
+ <div class="font-semibold text-xl">{{ loadError }}</div>
9034
+ <p-button icon="pi pi-refresh" label="Retry" (onClick)="reloadExtension()"></p-button>
9035
+ </div>
9036
+ } @else {
9037
+ <div #extensionContainer class="w-full"></div>
9038
+ }
9039
+ </div>
9040
+ } @else if (showSettings) {
9041
+ <div class="flex flex-col md:flex-row gap-8">
9042
+ <div class="md:w-1/2">
9043
+ <div class="card flex flex-col gap-4">
9044
+ <div class="font-semibold text-xl">{{ 'baseComponent.settings' | translate }}</div>
9045
+ <p-button icon="pi pi-save" label="Save" (onClick)="saveSettings(settings)"></p-button>
9046
+ </div>
9047
+ </div>
9048
+ </div>
9049
+ } @else if (showSecurity) {
9050
+ <security [controller]="controller" [id]="id" />
9051
+ }
9052
+ `
9053
+ }]
9054
+ }], ctorParameters: () => [], propDecorators: { extensionContainer: [{
9055
+ type: ViewChild,
9056
+ args: ['extensionContainer']
9057
+ }] } });
9058
+
9059
+ class ExtensionModuleHostComponent extends BaseModuleComponent {
9060
+ set extensionViewContainer(viewContainer) {
9061
+ this.viewContainer = viewContainer;
9062
+ this.queueRenderExtension();
9063
+ }
9064
+ get extensionKey() {
9065
+ return this.route.snapshot.paramMap.get('extensionKey') ?? '';
9066
+ }
9067
+ constructor() {
9068
+ super();
9069
+ this.injector = inject(Injector);
9070
+ this.environmentInjector = inject(EnvironmentInjector);
9071
+ this.extensionModulesApi = inject(ExtensionModulesApi);
9072
+ this.renderQueued = false;
9073
+ this.destroyed = false;
9074
+ this.loadError = null;
9075
+ this.subscriptions.push(this.topBarService.activeId$.subscribe(() => {
9076
+ this.updateExtensionContext();
9077
+ }));
9078
+ }
9079
+ async reloadExtension() {
9080
+ this.loadError = null;
9081
+ this.extensionMetadata = undefined;
9082
+ await this.loadExtensionMetadata();
9083
+ await this.renderExtension();
9084
+ }
9085
+ async onModuleInstanceChange() {
9086
+ await this.loadExtensionMetadata();
9087
+ await this.renderExtension();
9088
+ }
9089
+ onSecurityRightsChange() {
9090
+ this.updateExtensionContext();
9091
+ }
9092
+ ngOnDestroy() {
9093
+ this.destroyed = true;
9094
+ this.destroyExtensionComponent();
9095
+ super.ngOnDestroy();
9096
+ }
9097
+ async loadExtensionMetadata() {
9098
+ if (this.extensionMetadata) {
9099
+ return;
9100
+ }
9101
+ if (!this.extensionKey) {
9102
+ this.loadError = 'Extension key is missing.';
9103
+ return;
9104
+ }
9105
+ this.extensionMetadata = (await this.extensionModulesApi.getExtensionModuleByKey({
9106
+ extensionKey: this.extensionKey
9107
+ }));
9108
+ await this.loadExtensionTranslations(this.extensionMetadata);
9109
+ }
9110
+ async renderExtension() {
9111
+ if (this.destroyed || !this.viewContainer) {
9112
+ return;
9113
+ }
9114
+ try {
9115
+ this.loadError = null;
9116
+ await this.loadExtensionMetadata();
9117
+ if (!this.extensionMetadata) {
9118
+ return;
9119
+ }
9120
+ this.destroyExtensionComponent();
9121
+ if (this.extensionMetadata.loadType === 'moduleFederation') {
9122
+ await this.renderFederatedExtension(this.extensionMetadata);
9123
+ }
9124
+ else {
9125
+ this.extensionComponent = this.viewContainer.createComponent(CustomElementExtensionModuleHostComponent, {
9126
+ injector: this.injector,
9127
+ environmentInjector: this.environmentInjector
9128
+ });
9129
+ }
9130
+ }
9131
+ catch (error) {
9132
+ this.loadError = error instanceof Error ? error.message : 'Extension could not be loaded.';
9133
+ console.error(error);
9134
+ }
9135
+ }
9136
+ queueRenderExtension() {
9137
+ if (this.renderQueued) {
9138
+ return;
9139
+ }
9140
+ this.renderQueued = true;
9141
+ queueMicrotask(() => {
9142
+ this.renderQueued = false;
9143
+ void this.renderExtension();
9144
+ });
9145
+ }
9146
+ destroyExtensionComponent() {
9147
+ this.extensionComponent?.destroy();
9148
+ this.extensionComponent = undefined;
9149
+ this.extensionElement = undefined;
9150
+ this.viewContainer?.clear();
9151
+ }
9152
+ async renderFederatedExtension(metadata) {
9153
+ const { remoteEntryUrl, exposedModule, componentName } = metadata;
9154
+ if (!remoteEntryUrl || !exposedModule || !componentName) {
9155
+ this.loadError = 'Module Federation extension metadata is incomplete.';
9156
+ return;
9157
+ }
9158
+ const remoteModule = await loadRemoteModule({
9159
+ type: 'module',
9160
+ remoteEntry: remoteEntryUrl,
9161
+ exposedModule
9162
+ });
9163
+ const component = remoteModule[componentName];
9164
+ if (!component) {
9165
+ throw new Error(`Remote component '${componentName}' was not exported by '${exposedModule}'.`);
9166
+ }
9167
+ this.extensionComponent = this.viewContainer?.createComponent(component, {
9168
+ environmentInjector: this.environmentInjector
9169
+ });
9170
+ this.extensionElement = this.extensionComponent?.location.nativeElement;
9171
+ this.updateExtensionContext();
9172
+ }
9173
+ async loadExtensionTranslations(metadata) {
9174
+ const extensionKey = metadata.extensionKey ?? this.extensionKey;
9175
+ const apiBaseUrl = metadata.apiBaseUrl;
9176
+ if (!extensionKey || !apiBaseUrl) {
9177
+ return;
9178
+ }
9179
+ const lang = this.translateService.currentLang || this.layoutService.language() || 'en';
9180
+ for (const namespace of this.getExtensionTranslationNamespaces(metadata, extensionKey)) {
9181
+ const url = this.buildExtensionI18nUrl(apiBaseUrl, namespace, lang);
9182
+ try {
9183
+ await firstValueFrom(this.l10nService.loadTranslationsFromUrl(namespace, url, lang));
9184
+ return;
9185
+ }
9186
+ catch (error) {
9187
+ console.error(`No translations found for extension ${namespace}.${lang}.json`, error);
9188
+ }
9189
+ }
9190
+ }
9191
+ buildExtensionI18nUrl(apiBaseUrl, extensionKey, lang) {
9192
+ return `${apiBaseUrl.replace(/\/+$/, '')}/assets/i18n/${encodeURIComponent(extensionKey)}.${encodeURIComponent(lang)}.json`;
9193
+ }
9194
+ getExtensionTranslationNamespaces(metadata, extensionKey) {
9195
+ const namespaces = [extensionKey];
9196
+ const componentNamespace = this.toKebabCase(metadata.componentName?.replace(/Component$/, '') ?? '');
9197
+ if (componentNamespace && !namespaces.includes(componentNamespace)) {
9198
+ namespaces.push(componentNamespace);
9199
+ }
9200
+ return namespaces;
9201
+ }
9202
+ toKebabCase(value) {
9203
+ return value
9204
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
9205
+ .replace(/[\s_]+/g, '-')
9206
+ .toLowerCase();
9207
+ }
9208
+ updateExtensionContext() {
9209
+ if (!this.extensionElement || !this.extensionMetadata?.extensionKey) {
9210
+ return;
9211
+ }
9212
+ const context = {
9213
+ moduleInstanceId: this.id,
9214
+ extensionKey: this.extensionMetadata.extensionKey,
9215
+ apiBasePath: this.buildUrl(`api/extensions/${this.extensionMetadata.extensionKey}`),
9216
+ settings: this.settings,
9217
+ locale: this.layoutService.language(),
9218
+ theme: this.layoutService.layoutConfig(),
9219
+ user: this.securityService.getCurrentUser(),
9220
+ activeTabId: this.topBarService.activeId ?? 'content',
9221
+ permissions: {
9222
+ canRead: this.canRead,
9223
+ canEdit: this.canEdit,
9224
+ canDelete: this.canDelete
9225
+ }
9226
+ };
9227
+ Object.assign(this.extensionElement, { oipContext: context });
9228
+ emitOipContextChange(this.extensionElement, context);
9229
+ }
9230
+ buildUrl(path) {
9231
+ const baseUrl = document.getElementsByTagName('base')[0]?.href ?? `${window.location.origin}/`;
9232
+ return `${baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`}${path}`;
9233
+ }
9234
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ExtensionModuleHostComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
9235
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.24", type: ExtensionModuleHostComponent, isStandalone: true, selector: "ng-component", providers: [ExtensionModulesApi], viewQueries: [{ propertyName: "extensionViewContainer", first: true, predicate: ["extensionContainer"], descendants: true, read: ViewContainerRef }], usesInheritance: true, ngImport: i0, template: `
9236
+ @if (loadError) {
9237
+ <div class="card flex flex-col gap-4">
9238
+ <div class="font-semibold text-xl">{{ loadError }}</div>
9239
+ <p-button icon="pi pi-refresh" label="Retry" (onClick)="reloadExtension()"></p-button>
9240
+ </div>
9241
+ } @else {
9242
+ <ng-container #extensionContainer></ng-container>
9243
+ }
9244
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: 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"] }] }); }
9245
+ }
9246
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ExtensionModuleHostComponent, decorators: [{
9247
+ type: Component,
9248
+ args: [{
9249
+ standalone: true,
9250
+ imports: [CommonModule, Button],
9251
+ providers: [ExtensionModulesApi],
9252
+ template: `
9253
+ @if (loadError) {
9254
+ <div class="card flex flex-col gap-4">
9255
+ <div class="font-semibold text-xl">{{ loadError }}</div>
9256
+ <p-button icon="pi pi-refresh" label="Retry" (onClick)="reloadExtension()"></p-button>
9257
+ </div>
9258
+ } @else {
9259
+ <ng-container #extensionContainer></ng-container>
9260
+ }
9261
+ `
9262
+ }]
9263
+ }], ctorParameters: () => [], propDecorators: { extensionViewContainer: [{
9264
+ type: ViewChild,
9265
+ args: ['extensionContainer', { read: ViewContainerRef }]
9266
+ }] } });
9267
+
7515
9268
  /**
7516
9269
  * A route guard that ensures the user is authenticated and has a valid access token.
7517
9270
  * If the access token is expired, it attempts to refresh the session.
@@ -7520,24 +9273,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7520
9273
  class AuthGuardService {
7521
9274
  constructor() {
7522
9275
  this.oidcSecurityService = inject(SecurityService);
7523
- this.router = inject(Router);
7524
9276
  }
7525
9277
  /**
7526
9278
  * Checks whether the route can be activated.
7527
9279
  * - Returns `true` if the user is authenticated and the token is valid.
7528
9280
  * - Attempts to refresh the token if expired.
7529
- * - Redirects to `/unauthorized` if not authenticated or refresh fails.
9281
+ * - Starts the authorization flow if not authenticated or refresh fails.
7530
9282
  *
7531
- * @returns {Observable<boolean | UrlTree>} A stream resolving to true (allow), or UrlTree (redirect).
9283
+ * @returns {Observable<boolean>} A stream resolving to true (allow), or false after starting authorization.
7532
9284
  */
7533
9285
  canActivate(returnUrl = '/') {
7534
9286
  this.oidcSecurityService.auth();
7535
- return this.oidcSecurityService.isAuthenticated().pipe(map((authenticated) => authenticated
7536
- ? true
7537
- : this.router.createUrlTree(['/unauthorized'], { queryParams: { returnUrl: this.getReturnUrl(returnUrl) } })));
7538
- }
7539
- getReturnUrl(returnUrl) {
7540
- return returnUrl.startsWith('/unauthorized') ? '/' : returnUrl;
9287
+ return this.oidcSecurityService.isAuthenticated().pipe(map((authenticated) => {
9288
+ if (authenticated) {
9289
+ return true;
9290
+ }
9291
+ this.oidcSecurityService.authorize(returnUrl);
9292
+ return false;
9293
+ }));
7541
9294
  }
7542
9295
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: AuthGuardService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
7543
9296
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: AuthGuardService }); }
@@ -7546,6 +9299,103 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7546
9299
  type: Injectable
7547
9300
  }] });
7548
9301
 
9302
+ /**
9303
+ * Reads the rights the current user has on a module instance.
9304
+ *
9305
+ * Results are cached per instance and dropped whenever the authenticated user changes.
9306
+ */
9307
+ class ModuleInstanceRightsService {
9308
+ static { this.readRight = 'read'; }
9309
+ constructor() {
9310
+ this.menuApi = inject(MenuApi);
9311
+ this.securityService = inject(SecurityService);
9312
+ this.cache = new Map();
9313
+ this.cachedIdentity = undefined;
9314
+ this.securityService.payload.subscribe((payload) => {
9315
+ // Rights depend on both the user and the roles of the current session.
9316
+ const roles = payload?.realm_access?.roles ?? [];
9317
+ const identity = `${payload?.preferred_username ?? ''}|${[...roles].sort().join(',')}`;
9318
+ if (identity !== this.cachedIdentity) {
9319
+ this.cachedIdentity = identity;
9320
+ this.clearCache();
9321
+ }
9322
+ });
9323
+ }
9324
+ /**
9325
+ * Gets the rights of the current user on the given module instance.
9326
+ */
9327
+ getRights(moduleInstanceId) {
9328
+ const cached = this.cache.get(moduleInstanceId);
9329
+ if (cached) {
9330
+ return cached;
9331
+ }
9332
+ const request = this.menuApi
9333
+ .getModuleInstanceRights({ id: moduleInstanceId })
9334
+ .then((rights) => rights ?? [])
9335
+ .catch((error) => {
9336
+ this.cache.delete(moduleInstanceId);
9337
+ throw error;
9338
+ });
9339
+ this.cache.set(moduleInstanceId, request);
9340
+ return request;
9341
+ }
9342
+ /**
9343
+ * Checks whether the current user has the `read` right on the given module instance.
9344
+ */
9345
+ async canRead(moduleInstanceId) {
9346
+ const rights = await this.getRights(moduleInstanceId);
9347
+ return rights.includes(ModuleInstanceRightsService.readRight);
9348
+ }
9349
+ /**
9350
+ * Drops every cached result, for example after module security was changed.
9351
+ */
9352
+ clearCache() {
9353
+ this.cache.clear();
9354
+ }
9355
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ModuleInstanceRightsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
9356
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ModuleInstanceRightsService }); }
9357
+ }
9358
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ModuleInstanceRightsService, decorators: [{
9359
+ type: Injectable
9360
+ }], ctorParameters: () => [] });
9361
+
9362
+ /**
9363
+ * Guards module routes against direct navigation without the required rights.
9364
+ *
9365
+ * - Routes flagged with `data: { requireAdmin: true }` require the `admin` role.
9366
+ * - Routes carrying an `:id` segment require the `read` right on that module instance.
9367
+ * - Everything else is left to {@link AuthGuardService}.
9368
+ *
9369
+ * Denied navigation is redirected to the access denied page. This is a UX guard only:
9370
+ * the backend checks the same rights on every module endpoint.
9371
+ */
9372
+ const moduleAccessGuard = async (route) => {
9373
+ const router = inject(Router);
9374
+ const securityService = inject(SecurityService);
9375
+ const rightsService = inject(ModuleInstanceRightsService);
9376
+ const accessDenied = router.parseUrl('/access');
9377
+ if (route.data?.requireAdmin) {
9378
+ // Wait for the session payload so a direct URL entry does not read roles before they are loaded.
9379
+ await firstValueFrom(securityService.payload.pipe(filter((payload) => payload != null), take(1)));
9380
+ return securityService.isAdmin() ? true : accessDenied;
9381
+ }
9382
+ const routeId = route.paramMap.get('id');
9383
+ if (routeId == null) {
9384
+ return true;
9385
+ }
9386
+ const moduleInstanceId = Number(routeId);
9387
+ if (!Number.isFinite(moduleInstanceId)) {
9388
+ return accessDenied;
9389
+ }
9390
+ try {
9391
+ return (await rightsService.canRead(moduleInstanceId)) ? true : accessDenied;
9392
+ }
9393
+ catch (error) {
9394
+ console.error('Failed to load module instance rights', error);
9395
+ return accessDenied;
9396
+ }
9397
+ };
9398
+
7549
9399
  /**
7550
9400
  * Prime-ng table filter service
7551
9401
  */
@@ -7618,13 +9468,55 @@ function replaceDefaults(themes) {
7618
9468
  return provideAppThemes(themes, { mode: 'replaceDefaults' });
7619
9469
  }
7620
9470
 
9471
+ const defaultTheme = [
9472
+ {
9473
+ id: 'Oip',
9474
+ label: 'Oip',
9475
+ preset: definePreset(Aura, {
9476
+ components: {
9477
+ toolbar: {
9478
+ colorScheme: {
9479
+ light: {
9480
+ root: {
9481
+ borderRadius: '0.7rem'
9482
+ }
9483
+ },
9484
+ dark: {
9485
+ root: {
9486
+ borderRadius: '0.7rem'
9487
+ }
9488
+ }
9489
+ }
9490
+ }
9491
+ }
9492
+ }),
9493
+ primaryColors: {
9494
+ rose: primitive['red']
9495
+ },
9496
+ surfaceColors: {
9497
+ zinc: {
9498
+ 0: '#ffffff',
9499
+ 50: '#fafafa',
9500
+ 100: '#f4f4f5',
9501
+ 200: '#e4e4e7',
9502
+ 300: '#d4d4d8',
9503
+ 400: '#a1a1aa',
9504
+ 500: '#71717a',
9505
+ 600: '#52525b',
9506
+ 700: '#3f3f46',
9507
+ 800: '#27272a',
9508
+ 900: '#18181b',
9509
+ 950: '#09090b'
9510
+ }
9511
+ }
9512
+ }
9513
+ ];
9514
+
7621
9515
  const langIntercept = (req, next) => {
7622
9516
  const layoutService = inject(LayoutService);
7623
9517
  const securityService = inject(SecurityService);
7624
9518
  const lang = layoutService.language() ? layoutService.language() : 'en';
7625
- const headers = req.headers
7626
- .set('Accept-language', lang)
7627
- .set('X-Timezone', layoutService.timeZone());
9519
+ const headers = req.headers.set('Accept-language', lang).set('X-Timezone', layoutService.timeZone());
7628
9520
  const reqWithCredentials = req.clone({
7629
9521
  headers,
7630
9522
  withCredentials: true
@@ -7649,12 +9541,14 @@ function provideOip() {
7649
9541
  { provide: LocationStrategy, useClass: PathLocationStrategy },
7650
9542
  { provide: SecurityService, useClass: BffSecurityService },
7651
9543
  AuthGuardService,
9544
+ ModuleInstanceRightsService,
7652
9545
  MessageService,
7653
9546
  UserService,
7654
9547
  NotificationService,
7655
9548
  UserProfileApi,
7656
9549
  SecurityApi,
7657
9550
  NotificationApi,
9551
+ MenuApi,
7658
9552
  importProvidersFrom([
7659
9553
  TranslateModule.forRoot({
7660
9554
  loader: {
@@ -7673,10 +9567,9 @@ function provideOip() {
7673
9567
  class FolderModuleApi extends HttpClient {
7674
9568
  constructor() {
7675
9569
  super(...arguments);
7676
- this.getModuleInstanceSettings = (query, params = {}) => this.request({
9570
+ this.getModuleInstanceSettings = (params = {}) => this.request({
7677
9571
  path: `/api/folder-module/get-module-instance-settings`,
7678
9572
  method: "GET",
7679
- query: query,
7680
9573
  secure: true,
7681
9574
  format: "json",
7682
9575
  ...params,
@@ -7695,10 +9588,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7695
9588
  class IframeModuleApi extends HttpClient {
7696
9589
  constructor() {
7697
9590
  super(...arguments);
7698
- this.getModuleInstanceSettings = (query, params = {}) => this.request({
9591
+ this.getModuleInstanceSettings = (params = {}) => this.request({
7699
9592
  path: `/api/iframe-module/get-module-instance-settings`,
7700
9593
  method: "GET",
7701
- query: query,
7702
9594
  secure: true,
7703
9595
  format: "json",
7704
9596
  ...params,
@@ -7717,5 +9609,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImpo
7717
9609
  * Generated bundle index. Do not edit.
7718
9610
  */
7719
9611
 
7720
- export { APP_THEME_PRESETS, APP_THEME_PRESETS_MERGE_MODE, AppConfiguratorComponent, AppFloatingConfiguratorComponent, AppLayoutComponent, AppModulesComponent, AppTopbar, AppTopbarApplicationSwitcherComponent, ApplicationRegistryService, ApplicationsApi, ApplicationsComponent, AuthGuardService, BaseModuleComponent, BffSecurityService, ConfigComponent, ContentType, DbMigrationComponent, DiscussionComponent, ErrorComponent, FolderModuleApi, FooterComponent, HttpClient, IframeModuleApi, IframeModuleComponent, L10nService, LOGO_COMPONENT_TOKEN, LayoutService, LogoComponent, LogoService, MenuComponent, MenuService, MsgService, NotfoundComponent, NotificationApi, NotificationService, ProfileComponent, SecurityApi, SecurityComponent, SecurityService, SidebarComponent, TableFilterService, TopBarService, UnauthorizedComponent, UserNotificationsComponent, UserProfileApi, UserService, convertToPrimeNgDateFormat, langIntercept, mergeWithDefaults, provideAppThemes, provideLogoComponent, provideOip, replaceDefaults };
9612
+ export { APP_THEME_PRESETS, APP_THEME_PRESETS_MERGE_MODE, AccessComponent, AppConfiguratorComponent, AppFloatingConfiguratorComponent, AppLayoutComponent, AppModulesComponent, AppTopbar, AppTopbarApplicationSwitcherComponent, ApplicationRegistryService, ApplicationsApi, ApplicationsComponent, AuthGuardService, BaseModuleComponent, BffSecurityService, ConfigComponent, ContentType, CustomElementExtensionModuleHostComponent, DbMigrationComponent, DiscussionComponent, ErrorComponent, ExtensionLoaderService, ExtensionModuleHostComponent, FolderModuleApi, FooterComponent, HttpClient, IframeModuleApi, IframeModuleComponent, L10nService, LOGO_COMPONENT_TOKEN, LayoutService, LogoComponent, LogoService, MenuComponent, MenuService, ModuleInstanceRightsService, MsgService, NotfoundComponent, NotificationApi, NotificationService, OIP_EXTENSION_EVENTS, ProfileComponent, SecurityApi, SecurityComponent, SecurityService, SidebarComponent, TableFilterService, TopBarService, UnauthorizedComponent, UserNotificationsComponent, UserProfileApi, UserService, convertToPrimeNgDateFormat, defaultTheme, emitOipContextChange, emitOipError, emitOipNavigate, emitOipNotify, emitOipSettingsChange, emitOipTitleChange, langIntercept, mergeWithDefaults, moduleAccessGuard, provideAppThemes, provideLogoComponent, provideOip, replaceDefaults };
7721
9613
  //# sourceMappingURL=oip-common.mjs.map