oip-common 0.0.5 → 0.0.7

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.
Files changed (96) hide show
  1. package/README.md +3 -0
  2. package/ng-package.json +19 -0
  3. package/package.json +19 -31
  4. package/src/api/FolderModule.ts +124 -0
  5. package/src/api/Menu.ts +134 -0
  6. package/src/api/Module.ts +92 -0
  7. package/src/api/Security.ts +40 -0
  8. package/src/api/Service.ts +57 -0
  9. package/src/api/data-contracts.ts +186 -0
  10. package/src/api/http-client.ts +276 -0
  11. package/src/components/app-configurator.component.ts +491 -0
  12. package/src/components/app-floating-configurator.component.ts +47 -0
  13. package/src/components/app-modules.component.ts +144 -0
  14. package/src/components/app.layout.component.ts +130 -0
  15. package/src/components/auth/access/access.component.ts +42 -0
  16. package/src/components/auth/error/error.component.ts +42 -0
  17. package/src/components/auth/login/login.component.ts +120 -0
  18. package/src/components/auth/unauthorized/unauthorized.component.ts +51 -0
  19. package/src/components/base-module.component.ts +258 -0
  20. package/src/components/config.component.ts +131 -0
  21. package/src/components/db-migration/db-migration.component.ts +164 -0
  22. package/src/components/db-migration.component.ts +156 -0
  23. package/src/components/footer.component.ts +17 -0
  24. package/src/components/logo.component.ts +34 -0
  25. package/src/components/menu/menu-item-create-dialog.component.ts +119 -0
  26. package/src/components/menu/menu-item-edit-dialog.component.ts +124 -0
  27. package/src/components/menu/menu-item.component.ts +295 -0
  28. package/src/components/menu/menu.component.ts +85 -0
  29. package/src/components/notfound.component.ts +31 -0
  30. package/src/components/profile.component.ts +44 -0
  31. package/src/components/security.component.ts +102 -0
  32. package/src/components/sidebar.component.ts +12 -0
  33. package/src/components/top-bar.component.ts +147 -0
  34. package/src/dtos/context-menu-item.dto.ts +23 -0
  35. package/src/dtos/edit-module-instance.dto.ts +8 -0
  36. package/src/dtos/no-settings.dto.ts +4 -0
  37. package/src/dtos/put-security.dto.ts +6 -0
  38. package/src/dtos/security.dto.ts +6 -0
  39. package/src/dtos/top-bar.dto.ts +13 -0
  40. package/src/events/menu-change.event.ts +23 -0
  41. package/src/helpers/date.helper.ts +94 -0
  42. package/src/intercepts/i18n-intercept.service.ts +13 -0
  43. package/src/modules/http-loader.factory.ts +40 -0
  44. package/src/modules/secure.pipe.ts +19 -0
  45. package/src/public-api.ts +42 -0
  46. package/src/services/app-title.service.ts +22 -0
  47. package/src/services/app.layout.service.ts +236 -0
  48. package/src/services/app.menu.service.ts +64 -0
  49. package/src/services/auth.service.ts +58 -0
  50. package/src/services/base-data.service.ts +74 -0
  51. package/src/services/l10n.service.ts +71 -0
  52. package/src/services/msg.service.ts +76 -0
  53. package/src/services/security-data.service.ts +19 -0
  54. package/src/services/security-storage.service.ts +21 -0
  55. package/src/services/security.service.ts +116 -0
  56. package/src/services/top-bar.service.ts +44 -0
  57. package/src/services/user.service.ts +77 -0
  58. package/src/test.ts +11 -0
  59. package/src/user-api/UserProfile.ts +85 -0
  60. package/src/user-api/data-contracts.ts +42 -0
  61. package/src/user-api/http-client.ts +253 -0
  62. package/tsconfig.lib.json +12 -0
  63. package/tsconfig.lib.prod.json +10 -0
  64. package/tsconfig.spec.json +9 -0
  65. package/fesm2022/oip-common.mjs +0 -4267
  66. package/fesm2022/oip-common.mjs.map +0 -1
  67. package/index.d.ts +0 -1028
  68. /package/{assets → src/assets}/demo/code.scss +0 -0
  69. /package/{assets → src/assets}/demo/demo.scss +0 -0
  70. /package/{assets → src/assets}/demo/flags/flags.scss +0 -0
  71. /package/{assets → src/assets}/demo/flags/flags_responsive.png +0 -0
  72. /package/{assets → src/assets}/demo/images/access/asset-access.svg +0 -0
  73. /package/{assets → src/assets}/demo/images/error/asset-error.svg +0 -0
  74. /package/{assets → src/assets}/demo/images/flag/flag_placeholder.png +0 -0
  75. /package/{assets → src/assets}/favicon.svg +0 -0
  76. /package/{assets → src/assets}/i18n/app-modules.en.json +0 -0
  77. /package/{assets → src/assets}/i18n/app-modules.ru.json +0 -0
  78. /package/{assets → src/assets}/i18n/config.en.json +0 -0
  79. /package/{assets → src/assets}/i18n/config.ru.json +0 -0
  80. /package/{assets → src/assets}/layout/_core.scss +0 -0
  81. /package/{assets → src/assets}/layout/_footer.scss +0 -0
  82. /package/{assets → src/assets}/layout/_logo.scss +0 -0
  83. /package/{assets → src/assets}/layout/_main.scss +0 -0
  84. /package/{assets → src/assets}/layout/_menu.scss +0 -0
  85. /package/{assets → src/assets}/layout/_mixins.scss +0 -0
  86. /package/{assets → src/assets}/layout/_preloading.scss +0 -0
  87. /package/{assets → src/assets}/layout/_responsive.scss +0 -0
  88. /package/{assets → src/assets}/layout/_topbar.scss +0 -0
  89. /package/{assets → src/assets}/layout/_typography.scss +0 -0
  90. /package/{assets → src/assets}/layout/_utils.scss +0 -0
  91. /package/{assets → src/assets}/layout/layout.scss +0 -0
  92. /package/{assets → src/assets}/layout/variables/_common.scss +0 -0
  93. /package/{assets → src/assets}/layout/variables/_dark.scss +0 -0
  94. /package/{assets → src/assets}/layout/variables/_light.scss +0 -0
  95. /package/{assets → src/assets}/oip-common.scss +0 -0
  96. /package/{assets → src/assets}/tailwind.css +0 -0
@@ -0,0 +1,124 @@
1
+ import { Component, EventEmitter, inject, Input, Output } from '@angular/core';
2
+ import { ButtonModule } from 'primeng/button';
3
+ import { DialogModule } from 'primeng/dialog';
4
+ import { InputTextModule } from 'primeng/inputtext';
5
+ import { FormsModule } from '@angular/forms';
6
+ import { MenuService } from '../../services/app.menu.service';
7
+ import { SecurityDataService } from '../../services/security-data.service';
8
+ import { TranslatePipe } from '@ngx-translate/core';
9
+ import { EditModuleInstanceDto } from '../../dtos/edit-module-instance.dto';
10
+ import { MultiSelectModule } from 'primeng/multiselect';
11
+
12
+ @Component({
13
+ imports: [ButtonModule, DialogModule, InputTextModule, FormsModule, TranslatePipe, MultiSelectModule],
14
+ selector: 'menu-item-edit-dialog',
15
+ standalone: true,
16
+ template: `
17
+ <p-dialog
18
+ header="{{ 'menuItemEditDialogComponent.header' | translate }}"
19
+ [modal]="true"
20
+ [style]="{ width: '40rem' }"
21
+ [(visible)]="visible">
22
+ <div class="flex items-center gap-4 mb-4 mt-1">
23
+ <label class="font-semibold w-1/3" for="oip-menu-item-edit-dialog-menu-input">
24
+ {{ 'menuItemEditDialogComponent.label' | translate }}
25
+ </label>
26
+ <input
27
+ autocomplete="off"
28
+ class="flex-auto"
29
+ id="oip-menu-item-edit-dialog-menu-input"
30
+ pInputText
31
+ [(ngModel)]="item.label" />
32
+ </div>
33
+
34
+ <div class="flex items-center gap-4 mb-4">
35
+ <label class="font-semibold w-1/3" for="oip-menu-item-edit-dialog-icon">
36
+ {{ 'menuItemEditDialogComponent.icon' | translate }}
37
+ </label>
38
+ <i class="{{ item.icon }}"></i>
39
+ <input class="flex-auto" id="oip-menu-item-edit-dialog-icon" pInputText [(ngModel)]="item.icon" />
40
+ </div>
41
+
42
+ <div class="flex items-center gap-4 mb-4">
43
+ <label class="font-semibold w-1/3" for="security">
44
+ {{ 'menuItemEditDialogComponent.security' | translate }}
45
+ </label>
46
+ <p-multiSelect
47
+ appendTo="body"
48
+ class="flex-auto"
49
+ id="oip-menu-item-edit-dialog-roles-multi-select"
50
+ placeholder="Select roles"
51
+ [maxSelectedLabels]="10"
52
+ [options]="roles"
53
+ [(ngModel)]="item.viewRoles" />
54
+ </div>
55
+
56
+ <div class="flex justify-end gap-2">
57
+ <p-button
58
+ id="oip-menu-item-edit-dialog-cancel-edit-button"
59
+ label="{{ 'menuItemEditDialogComponent.cancel' | translate }}"
60
+ severity="secondary"
61
+ (click)="changeVisible()"
62
+ (keydown)="changeVisible()" />
63
+ <p-button
64
+ id="oip-menu-item-edit-dialog-save-edit-button"
65
+ label="{{ 'menuItemEditDialogComponent.save' | translate }}"
66
+ (click)="save()"
67
+ (keydown)="save()" />
68
+ </div>
69
+ </p-dialog>
70
+ `
71
+ })
72
+ export class MenuItemEditDialogComponent {
73
+ private readonly menuService = inject(MenuService);
74
+ private readonly securityDataService = inject(SecurityDataService);
75
+
76
+ @Input() visible!: boolean;
77
+ @Output() visibleChange = new EventEmitter<boolean>();
78
+
79
+ modules: any[] = [];
80
+ roles: string[] = [];
81
+ item: EditModuleInstanceDto = {
82
+ icon: '',
83
+ label: '',
84
+ viewRoles: [''],
85
+ moduleId: 0,
86
+ moduleInstanceId: 0,
87
+ parentId: 0
88
+ };
89
+
90
+ changeVisible() {
91
+ this.visible = !this.visible;
92
+ this.visibleChange.emit(this.visible);
93
+ }
94
+
95
+ async save() {
96
+ await this.menuService.editModuleInstance(this.item);
97
+ await this.menuService.loadMenu();
98
+ this.hide();
99
+ }
100
+
101
+ hide() {
102
+ this.visible = false;
103
+ this.visibleChange.emit(this.visible);
104
+ }
105
+
106
+ async showDialog() {
107
+ this.item = {
108
+ moduleInstanceId: this.menuService.contextMenuItem?.moduleInstanceId,
109
+ moduleId: this.menuService.contextMenuItem?.moduleId,
110
+ parentId: this.menuService.contextMenuItem?.parentId,
111
+ label: this.menuService.contextMenuItem?.label,
112
+ icon: this.menuService.contextMenuItem?.icon,
113
+ viewRoles: this.menuService.contextMenuItem?.securities
114
+ };
115
+
116
+ this.roles = await this.securityDataService.getRealmRoles();
117
+ this.menuService.getModules().then((data) => {
118
+ this.modules = data;
119
+ });
120
+
121
+ this.visible = true;
122
+ this.visibleChange.emit(this.visible);
123
+ }
124
+ }
@@ -0,0 +1,295 @@
1
+ import { ChangeDetectorRef, Component, HostBinding, inject, Input, OnDestroy, OnInit } from '@angular/core';
2
+ import { NavigationEnd, Router, RouterLinkActive, RouterLink } from '@angular/router';
3
+ import { animate, state, style, transition, trigger } from '@angular/animations';
4
+ import { Subscription } from 'rxjs';
5
+ import { filter } from 'rxjs/operators';
6
+ import { LayoutService } from '../../services/app.layout.service';
7
+ import { MenuService } from '../../services/app.menu.service';
8
+ import { RippleModule } from 'primeng/ripple';
9
+ import { NgIf, NgClass, NgFor } from '@angular/common';
10
+ import { ConfirmationService, ContextMenuService, MenuItem, MenuItemCommandEvent, PrimeIcons } from 'primeng/api';
11
+ import { MenuItemCreateDialogComponent } from './menu-item-create-dialog.component';
12
+ import { ContextMenu, ContextMenuModule } from 'primeng/contextmenu';
13
+ import { MsgService } from '../../services/msg.service';
14
+ import { MenuItemEditDialogComponent } from './menu-item-edit-dialog.component';
15
+ import { ContextMenuItemDto } from '../../dtos/context-menu-item.dto';
16
+ import { TranslateService } from '@ngx-translate/core';
17
+ import { ConfirmDialog } from 'primeng/confirmdialog';
18
+ import { Menu } from '../../api/Menu';
19
+ import { MenuDeleteModuleInstanceParams } from '../../api/data-contracts';
20
+
21
+ interface MenuItemComponentTranslation {
22
+ delete: string;
23
+ edit: string;
24
+ new: string;
25
+ deleteItemConfirmHeader: string;
26
+ deleteItemConfirmMessage: string;
27
+ deleteItemSuccessMessage: string;
28
+ deleteItemConfirmRejectButtonPropsLabel: string;
29
+ deleteItemConfirmAcceptButtonPropsLabel: string;
30
+ }
31
+
32
+ @Component({
33
+ // eslint-disable-next-line @angular-eslint/component-selector
34
+ selector: '[app-menuitem]',
35
+ template: `
36
+ <ng-container>
37
+ <p-confirm-dialog />
38
+ <div
39
+ *ngIf="root && item.visible !== false"
40
+ class="layout-menuitem-root-text"
41
+ (contextmenu)="onContextMenu($event, item)">
42
+ {{ item.label }}
43
+ </div>
44
+ <a
45
+ *ngIf="(!item.routerLink || item.items) && item.visible !== false"
46
+ pRipple
47
+ tabindex="0"
48
+ [attr.href]="item.url"
49
+ [attr.target]="item.target"
50
+ [ngClass]="item.class"
51
+ (click)="itemClick($event)">
52
+ <i class="layout-menuitem-icon" [ngClass]="item.icon"></i>
53
+ <span class="layout-menuitem-text">{{ item.label }}</span>
54
+ <i *ngIf="item.items" class="pi pi-fw pi-angle-down layout-submenu-toggler"></i>
55
+ </a>
56
+ <a
57
+ *ngIf="item.routerLink && !item.items && item.visible !== false"
58
+ pRipple
59
+ routerLinkActive="active-route"
60
+ tabindex="0"
61
+ [attr.target]="item.target"
62
+ [fragment]="item.fragment"
63
+ [ngClass]="item.class"
64
+ [preserveFragment]="item.preserveFragment"
65
+ [queryParams]="item.queryParams"
66
+ [queryParamsHandling]="item.queryParamsHandling"
67
+ [replaceUrl]="item.replaceUrl"
68
+ [routerLink]="item.routerLink"
69
+ [routerLinkActiveOptions]="
70
+ item.routerLinkActiveOptions || {
71
+ paths: 'exact',
72
+ queryParams: 'ignored',
73
+ matrixParams: 'ignored',
74
+ fragment: 'ignored'
75
+ }
76
+ "
77
+ [skipLocationChange]="item.skipLocationChange"
78
+ [state]="item.state"
79
+ (click)="itemClick($event)"
80
+ (contextmenu)="onContextMenu($event, item)">
81
+ <i class="layout-menuitem-icon" [ngClass]="item.icon"></i>
82
+ <span class="layout-menuitem-text">{{ item.label }}</span>
83
+ <i *ngIf="item.items" class="pi pi-fw pi-angle-down layout-submenu-toggler"></i>
84
+ </a>
85
+
86
+ <ul
87
+ *ngIf="item.items && item.visible !== false"
88
+ [@children]="submenuAnimation"
89
+ (contextmenu)="onContextMenu($event, item)">
90
+ <ng-template let-child let-i="index" ngFor [ngForOf]="item.items">
91
+ <li
92
+ app-menuitem
93
+ [class]="child.badgeClass"
94
+ [contextMenu]="contextMenu"
95
+ [index]="i"
96
+ [item]="child"
97
+ [menuItemCreateDialogComponent]="menuItemCreateDialogComponent"
98
+ [menuItemEditDialogComponent]="menuItemEditDialogComponent"
99
+ [parentKey]="key"></li>
100
+ </ng-template>
101
+ </ul>
102
+ </ng-container>
103
+ `,
104
+ animations: [
105
+ trigger('children', [
106
+ state(
107
+ 'collapsed',
108
+ style({
109
+ height: '0'
110
+ })
111
+ ),
112
+ state(
113
+ 'expanded',
114
+ style({
115
+ height: '*'
116
+ })
117
+ ),
118
+ transition('collapsed <=> expanded', animate('400ms cubic-bezier(0.86, 0, 0.07, 1)'))
119
+ ])
120
+ ],
121
+ imports: [NgIf, RippleModule, NgClass, RouterLinkActive, RouterLink, NgFor, ContextMenuModule, ConfirmDialog],
122
+ providers: [ConfirmationService]
123
+ })
124
+ export class MenuItemComponent implements OnInit, OnDestroy {
125
+ private readonly layoutService = inject(LayoutService);
126
+ private readonly translateService = inject(TranslateService);
127
+ private readonly confirmationService = inject(ConfirmationService);
128
+ private readonly msgService = inject(MsgService);
129
+ private readonly menuDataService = inject(Menu);
130
+
131
+ @Input() item: ContextMenuItemDto;
132
+ @Input() index!: number;
133
+ @Input() @HostBinding('class.layout-root-menuitem') root!: boolean;
134
+ @Input() parentKey!: string;
135
+ @Input() menuItemCreateDialogComponent: MenuItemCreateDialogComponent;
136
+ @Input() menuItemEditDialogComponent: MenuItemEditDialogComponent;
137
+ @Input() contextMenu: ContextMenu;
138
+
139
+ private active = false;
140
+ private subscriptions: Subscription[] = [];
141
+ private localization: MenuItemComponentTranslation = {} as MenuItemComponentTranslation;
142
+
143
+ protected key: string = '';
144
+
145
+ constructor(
146
+ private readonly cd: ChangeDetectorRef,
147
+ public router: Router,
148
+ private readonly menuService: MenuService
149
+ ) {
150
+ this.subscriptions.push(
151
+ this.menuService.menuSource$.subscribe((value) => {
152
+ Promise.resolve(null).then(() => {
153
+ if (value.routeEvent) {
154
+ this.active = value.key === this.key || value.key.startsWith(this.key + '-');
155
+ } else if (value.key !== this.key && !value.key.startsWith(this.key + '-')) {
156
+ this.active = false;
157
+ }
158
+ });
159
+ })
160
+ );
161
+
162
+ this.subscriptions.push(
163
+ this.menuService.resetSource$.subscribe(() => {
164
+ this.active = false;
165
+ })
166
+ );
167
+
168
+ this.subscriptions.push(
169
+ this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe((params) => {
170
+ if (this.item.routerLink) {
171
+ this.updateActiveStateFromRoute();
172
+ }
173
+ })
174
+ );
175
+
176
+ this.subscriptions.push(
177
+ this.translateService.get('menuItemComponent').subscribe((value: MenuItemComponentTranslation) => {
178
+ this.localization = value;
179
+ })
180
+ );
181
+ }
182
+
183
+ ngOnInit() {
184
+ this.key = this.parentKey ? this.parentKey + '-' + this.index : String(this.index);
185
+
186
+ if (this.item.routerLink) {
187
+ this.updateActiveStateFromRoute();
188
+ }
189
+ }
190
+
191
+ updateActiveStateFromRoute() {
192
+ const activeRoute = this.router.isActive(this.item.routerLink[0], {
193
+ paths: 'exact',
194
+ queryParams: 'ignored',
195
+ matrixParams: 'ignored',
196
+ fragment: 'ignored'
197
+ });
198
+
199
+ if (activeRoute) {
200
+ this.menuService.onMenuStateChange({
201
+ key: this.key,
202
+ item: this.item,
203
+ routeEvent: true
204
+ });
205
+ }
206
+ }
207
+
208
+ itemClick(event: Event) {
209
+ // avoid processing disabled items
210
+ if (this.item.disabled) {
211
+ event.preventDefault();
212
+ return;
213
+ }
214
+
215
+ // execute command
216
+ if (this.item.command) {
217
+ this.item.command({ originalEvent: event, item: this.item });
218
+ }
219
+
220
+ // toggle active state
221
+ if (this.item.items) {
222
+ this.active = !this.active;
223
+ }
224
+
225
+ this.menuService.onMenuStateChange({ key: this.key, item: this.item });
226
+ }
227
+
228
+ get submenuAnimation() {
229
+ return this.root || this.active ? 'expanded' : 'collapsed';
230
+ }
231
+
232
+ @HostBinding('class.active-menuitem')
233
+ get activeClass() {
234
+ return this.active && !this.root;
235
+ }
236
+
237
+ ngOnDestroy() {
238
+ this.subscriptions.map((s) => s.unsubscribe());
239
+ }
240
+
241
+ private newClick(e: MenuItemCommandEvent) {
242
+ this.menuItemCreateDialogComponent.showDialog();
243
+ }
244
+
245
+ onContextMenu($event: MouseEvent, item: any) {
246
+ this.menuService.contextMenuItem = item;
247
+ this.contextMenu.model = [
248
+ {
249
+ label: this.localization.new,
250
+ icon: PrimeIcons.PLUS,
251
+ command: (event) => this.newClick(event)
252
+ },
253
+ {
254
+ label: this.localization.edit,
255
+ icon: PrimeIcons.FILE_EDIT,
256
+ command: (event) => this.editClick(event)
257
+ },
258
+ { separator: true },
259
+ {
260
+ label: this.localization.delete,
261
+ icon: PrimeIcons.TRASH,
262
+ command: (event) => this.deleteItem(event)
263
+ }
264
+ ];
265
+ this.contextMenu.show($event);
266
+ }
267
+
268
+ private deleteItem(event: MenuItemCommandEvent) {
269
+ this.confirmationService.confirm({
270
+ header: this.localization.deleteItemConfirmHeader,
271
+ message: this.localization.deleteItemConfirmMessage,
272
+ icon: PrimeIcons.TRASH,
273
+ rejectButtonProps: {
274
+ label: this.localization.deleteItemConfirmRejectButtonPropsLabel,
275
+ severity: 'secondary',
276
+ outlined: true
277
+ },
278
+ acceptButtonProps: {
279
+ label: this.localization.deleteItemConfirmAcceptButtonPropsLabel,
280
+ severity: 'danger'
281
+ },
282
+ accept: async () => {
283
+ await this.menuDataService.menuDeleteModuleInstance({
284
+ id: this.menuService.contextMenuItem?.moduleInstanceId
285
+ } as MenuDeleteModuleInstanceParams);
286
+ this.msgService.success(this.localization.deleteItemSuccessMessage);
287
+ await this.menuService.loadMenu();
288
+ }
289
+ });
290
+ }
291
+
292
+ private editClick(event: MenuItemCommandEvent) {
293
+ this.menuItemEditDialogComponent.showDialog();
294
+ }
295
+ }
@@ -0,0 +1,85 @@
1
+ import { Component, inject, OnInit, ViewChild } from '@angular/core';
2
+ import { MenuService } from '../../services/app.menu.service';
3
+ import { ButtonModule } from 'primeng/button';
4
+ import { SecurityService } from '../../services/security.service';
5
+ import { ContextMenu, ContextMenuModule } from 'primeng/contextmenu';
6
+ import { DialogModule } from 'primeng/dialog';
7
+ import { MenuItemCommandEvent, PrimeIcons } from 'primeng/api';
8
+ import { InputTextModule } from 'primeng/inputtext';
9
+ import { FormsModule } from '@angular/forms';
10
+ import { MenuItemComponent } from './menu-item.component';
11
+ import { MenuItemCreateDialogComponent } from './menu-item-create-dialog.component';
12
+ import { TranslateService } from '@ngx-translate/core';
13
+ import { MenuItemEditDialogComponent } from './menu-item-edit-dialog.component';
14
+ import { Menu } from '../../api/Menu';
15
+
16
+ @Component({
17
+ imports: [
18
+ MenuItemComponent,
19
+ ButtonModule,
20
+ ContextMenuModule,
21
+ DialogModule,
22
+ InputTextModule,
23
+ MenuItemCreateDialogComponent,
24
+ FormsModule,
25
+ MenuItemEditDialogComponent
26
+ ],
27
+ providers: [Menu],
28
+ selector: 'app-menu',
29
+ standalone: true,
30
+ template: ` <div #empty class="layout-sidebar" (contextmenu)="onContextMenu($event)">
31
+ <ul class="layout-menu">
32
+ @for (item of menuService.menu; track item; let i = $index) {
33
+ <ng-container>
34
+ @if (item.separator) {
35
+ <li class="menu-separator"></li>
36
+ } @else {
37
+ <li
38
+ app-menuitem
39
+ [contextMenu]="contextMenu"
40
+ [index]="i"
41
+ [item]="item"
42
+ [menuItemCreateDialogComponent]="menuItemCreateDialogComponent"
43
+ [menuItemEditDialogComponent]="menuItemEditDialogComponent"
44
+ [root]="true"></li>
45
+ }
46
+ </ng-container>
47
+ }
48
+ </ul>
49
+ </div>
50
+ <p-contextMenu [target]="empty" />
51
+ @if (securityService.isAdmin) {
52
+ <menu-item-create-dialog />
53
+ <menu-item-edit-dialog />
54
+ }`
55
+ })
56
+ export class MenuComponent implements OnInit {
57
+ readonly menuService = inject(MenuService);
58
+ readonly securityService = inject(SecurityService);
59
+ readonly translateService = inject(TranslateService);
60
+
61
+ @ViewChild(MenuItemCreateDialogComponent)
62
+ menuItemCreateDialogComponent: MenuItemCreateDialogComponent;
63
+ @ViewChild(MenuItemEditDialogComponent)
64
+ menuItemEditDialogComponent: MenuItemEditDialogComponent;
65
+ @ViewChild(ContextMenu) contextMenu: ContextMenu;
66
+
67
+ ngOnInit() {
68
+ this.menuService.loadMenu().then();
69
+ }
70
+
71
+ private newClick(e: MenuItemCommandEvent) {
72
+ this.menuItemCreateDialogComponent.showDialog();
73
+ }
74
+
75
+ onContextMenu($event: MouseEvent) {
76
+ this.menuService.contextMenuItem = null;
77
+ this.contextMenu.model = [
78
+ {
79
+ label: this.translateService.instant('menuComponent.new'),
80
+ icon: PrimeIcons.PLUS,
81
+ command: (event) => this.newClick(event)
82
+ }
83
+ ];
84
+ }
85
+ }
@@ -0,0 +1,31 @@
1
+ import { Component } from '@angular/core';
2
+ import { RouterLink } from '@angular/router';
3
+ import { LogoComponent } from './logo.component';
4
+ import { Button } from 'primeng/button';
5
+ import { AppFloatingConfiguratorComponent } from './app-floating-configurator.component';
6
+
7
+ @Component({
8
+ selector: 'app-notfound',
9
+ template: ` <app-floating-configurator />
10
+ <div class="flex items-center justify-center min-h-screen overflow-hidden">
11
+ <div class="flex flex-col items-center justify-center">
12
+ <div
13
+ style="border-radius: 56px; padding: 0.3rem; background: linear-gradient(180deg, color-mix(in srgb, var(--primary-color), transparent 60%) 10%, var(--surface-ground) 30%)">
14
+ <div
15
+ class="w-full bg-surface-0 dark:bg-surface-900 py-20 px-8 sm:px-20 flex flex-col items-center"
16
+ style="border-radius: 53px">
17
+ <div class="flex flex-col items-center justify-center">
18
+ <logo height="96" width="96"></logo>
19
+ </div>
20
+ <span class="text-primary font-bold text-3xl">404</span>
21
+ <h1 class="text-surface-900 dark:text-surface-0 font-bold text-3xl lg:text-5xl mb-2">Not Found</h1>
22
+ <div class="text-surface-600 dark:text-surface-200 mb-8">Requested resource is not available.</div>
23
+ <p-button id="oip-app-notfound-go-to-home-button" label="Go to home" routerLink="/" />
24
+ </div>
25
+ </div>
26
+ </div>
27
+ </div>`,
28
+ imports: [RouterLink, LogoComponent, Button, AppFloatingConfiguratorComponent],
29
+ standalone: true
30
+ })
31
+ export class NotfoundComponent {}
@@ -0,0 +1,44 @@
1
+ import { Component, inject } from '@angular/core';
2
+ import { FileUploadModule } from 'primeng/fileupload';
3
+ import { ImageModule } from 'primeng/image';
4
+ import { AvatarModule } from 'primeng/avatar';
5
+ import { MsgService } from '../services/msg.service';
6
+ import { UserService } from '../services/user.service';
7
+ import { TranslatePipe, TranslateService } from '@ngx-translate/core';
8
+
9
+ @Component({
10
+ selector: 'user-profile',
11
+ standalone: true,
12
+ imports: [FileUploadModule, ImageModule, ImageModule, FileUploadModule, ImageModule, AvatarModule, TranslatePipe],
13
+ template: `
14
+ <p-avatar
15
+ class="mr-2"
16
+ id="oip-user-profile-photo-avatar"
17
+ shape="circle"
18
+ size="xlarge"
19
+ [image]="userService.photoLoaded ? userService.photo : null" />
20
+ <div class="mt-2">
21
+ <p-fileupload
22
+ accept="image/*"
23
+ chooseIcon="pi pi-upload"
24
+ chooseLabel="{{ 'profileComponent.changePhoto' | translate }}"
25
+ id="oip-user-profile-file-upload"
26
+ maxFileSize="1000000"
27
+ mode="basic"
28
+ name="files"
29
+ url="/api/user-profile/post-user-photo"
30
+ withCredentials="true"
31
+ [auto]="true"
32
+ (onUpload)="onBasicUploadAuto($event)" />
33
+ </div>
34
+ `
35
+ })
36
+ export class ProfileComponent {
37
+ readonly userService = inject(UserService);
38
+ readonly msgService = inject(MsgService);
39
+ readonly translateService = inject(TranslateService);
40
+
41
+ onBasicUploadAuto($event) {
42
+ this.msgService.success(this.translateService.instant('profileComponent.successfullyUploaded'));
43
+ }
44
+ }
@@ -0,0 +1,102 @@
1
+ import { Component, inject, Input, OnDestroy, OnInit } from '@angular/core';
2
+ import { MultiSelectModule } from 'primeng/multiselect';
3
+ import { TooltipModule } from 'primeng/tooltip';
4
+ import { ButtonModule } from 'primeng/button';
5
+ import { FormsModule } from '@angular/forms';
6
+ import { MsgService } from './../services/msg.service';
7
+ import { SecurityDataService } from './../services/security-data.service';
8
+ import { PutSecurityDto } from './../dtos/put-security.dto';
9
+ import { TranslatePipe, TranslateService } from '@ngx-translate/core';
10
+
11
+ @Component({
12
+ selector: 'security',
13
+ template: `
14
+ <div class="flex flex-col md:flex-row gap-8">
15
+ <div class="md:w-1/2">
16
+ <div class="card flex flex-col gap-4">
17
+ <div class="font-semibold text-xl">
18
+ {{ 'securityComponent.security' | translate }}
19
+ </div>
20
+ @for (item of securityData; track item.name) {
21
+ <div class="flex flex-col gap-2">
22
+ <label htmlFor="oip-security-multiselect-{{ item.name }}">
23
+ {{ item.name }}
24
+ <span class="pi pi-question-circle" pTooltip="{{ item.description }}" tooltipPosition="right"></span>
25
+ </label>
26
+ <p-multiSelect
27
+ id="oip-security-multiselect-{{ item.name }}"
28
+ placeholder="Select roles"
29
+ [maxSelectedLabels]="10"
30
+ [options]="roles"
31
+ [(ngModel)]="item.roles" />
32
+ </div>
33
+ }
34
+ <div class="flex justify-content-end flex-wrap">
35
+ <p-button
36
+ icon="pi pi-save"
37
+ id="oip-security-save-button"
38
+ label="{{ 'securityComponent.save' | translate }}"
39
+ (click)="saveClick()"
40
+ (keydown)="saveKeyDown($event)" />
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ `,
46
+ imports: [MultiSelectModule, TooltipModule, FormsModule, ButtonModule, TranslatePipe],
47
+ standalone: true
48
+ })
49
+ export class SecurityComponent implements OnInit, OnDestroy {
50
+ private readonly msgService = inject(MsgService);
51
+ private readonly dataService = inject(SecurityDataService);
52
+ private readonly translateService = inject(TranslateService);
53
+ securityData: any[];
54
+ @Input() id: number;
55
+ @Input() controller: string;
56
+ roles: string[] = [];
57
+
58
+ ngOnDestroy(): void {
59
+ // on destroy
60
+ }
61
+
62
+ ngOnInit(): void {
63
+ if (!this.id) {
64
+ this.msgService.error('Module id not passed!');
65
+ }
66
+ if (!this.controller) {
67
+ this.msgService.error('Controller not passed!');
68
+ }
69
+ this.dataService.getSecurity(this.controller, this.id).then(
70
+ (result) => {
71
+ this.securityData = result;
72
+ },
73
+ (error) => this.msgService.error(error)
74
+ );
75
+
76
+ this.dataService.getRealmRoles().then(
77
+ (result) => {
78
+ this.roles = result;
79
+ },
80
+ (error) => this.msgService.error(error)
81
+ );
82
+ }
83
+
84
+ saveClick() {
85
+ const request: PutSecurityDto = {
86
+ id: this.id,
87
+ securities: this.securityData
88
+ };
89
+ this.dataService.saveSecurity(this.controller, request).then(
90
+ (result) => {
91
+ this.msgService.success(this.translateService.instant('securityComponent.savedSecurity'));
92
+ },
93
+ (error) => this.msgService.error(error)
94
+ );
95
+ }
96
+
97
+ saveKeyDown($event: KeyboardEvent) {
98
+ if ($event.key === 'Enter' || $event.key === 'Space') {
99
+ this.saveKeyDown(null);
100
+ }
101
+ }
102
+ }