oip-common 0.0.6 → 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 (105) hide show
  1. package/ng-package.json +19 -0
  2. package/package.json +19 -38
  3. package/src/api/FolderModule.ts +124 -0
  4. package/src/api/Menu.ts +134 -0
  5. package/src/api/Module.ts +92 -0
  6. package/src/api/Security.ts +40 -0
  7. package/src/api/Service.ts +57 -0
  8. package/src/api/data-contracts.ts +186 -0
  9. package/src/api/http-client.ts +276 -0
  10. package/src/components/app-configurator.component.ts +491 -0
  11. package/src/components/app-floating-configurator.component.ts +47 -0
  12. package/src/components/app-modules.component.ts +144 -0
  13. package/src/components/app.layout.component.ts +130 -0
  14. package/src/components/auth/access/access.component.ts +42 -0
  15. package/src/components/auth/error/error.component.ts +42 -0
  16. package/src/components/auth/login/login.component.ts +120 -0
  17. package/src/components/auth/unauthorized/unauthorized.component.ts +51 -0
  18. package/src/components/base-module.component.ts +258 -0
  19. package/src/components/config.component.ts +131 -0
  20. package/src/components/db-migration/db-migration.component.ts +164 -0
  21. package/src/components/db-migration.component.ts +156 -0
  22. package/src/components/footer.component.ts +17 -0
  23. package/src/components/logo.component.ts +34 -0
  24. package/src/components/menu/menu-item-create-dialog.component.ts +119 -0
  25. package/src/components/menu/menu-item-edit-dialog.component.ts +124 -0
  26. package/src/components/menu/menu-item.component.ts +295 -0
  27. package/src/components/menu/menu.component.ts +85 -0
  28. package/src/components/notfound.component.ts +31 -0
  29. package/src/components/profile.component.ts +44 -0
  30. package/src/components/security.component.ts +102 -0
  31. package/src/components/sidebar.component.ts +12 -0
  32. package/src/components/top-bar.component.ts +147 -0
  33. package/src/dtos/context-menu-item.dto.ts +23 -0
  34. package/src/dtos/edit-module-instance.dto.ts +8 -0
  35. package/src/dtos/no-settings.dto.ts +4 -0
  36. package/src/dtos/put-security.dto.ts +6 -0
  37. package/src/dtos/security.dto.ts +6 -0
  38. package/src/dtos/top-bar.dto.ts +13 -0
  39. package/src/events/menu-change.event.ts +23 -0
  40. package/src/helpers/date.helper.ts +94 -0
  41. package/src/intercepts/i18n-intercept.service.ts +13 -0
  42. package/src/modules/http-loader.factory.ts +40 -0
  43. package/src/modules/secure.pipe.ts +19 -0
  44. package/src/public-api.ts +42 -0
  45. package/src/services/app-title.service.ts +22 -0
  46. package/src/services/app.layout.service.ts +236 -0
  47. package/src/services/app.menu.service.ts +64 -0
  48. package/src/services/auth.service.ts +58 -0
  49. package/src/services/base-data.service.ts +74 -0
  50. package/src/services/l10n.service.ts +71 -0
  51. package/src/services/msg.service.ts +76 -0
  52. package/src/services/security-data.service.ts +19 -0
  53. package/src/services/security-storage.service.ts +21 -0
  54. package/src/services/security.service.ts +116 -0
  55. package/src/services/top-bar.service.ts +44 -0
  56. package/src/services/user.service.ts +77 -0
  57. package/src/test.ts +11 -0
  58. package/src/user-api/UserProfile.ts +85 -0
  59. package/src/user-api/data-contracts.ts +42 -0
  60. package/src/user-api/http-client.ts +253 -0
  61. package/templates/api.ejs +30 -0
  62. package/templates/data-contract-jsdoc.ejs +37 -0
  63. package/templates/data-contracts.ejs +52 -0
  64. package/templates/enum-data-contract.ejs +12 -0
  65. package/templates/http-client.ejs +245 -0
  66. package/templates/interface-data-contract.ejs +10 -0
  67. package/templates/object-field-jsdoc.ejs +28 -0
  68. package/templates/procedure-call.ejs +100 -0
  69. package/templates/route-docs.ejs +29 -0
  70. package/templates/route-name.ejs +42 -0
  71. package/templates/route-type.ejs +23 -0
  72. package/templates/route-types.ejs +18 -0
  73. package/templates/type-data-contract.ejs +15 -0
  74. package/tsconfig.lib.json +12 -0
  75. package/tsconfig.lib.prod.json +10 -0
  76. package/tsconfig.spec.json +9 -0
  77. /package/{assets → src/assets}/demo/code.scss +0 -0
  78. /package/{assets → src/assets}/demo/demo.scss +0 -0
  79. /package/{assets → src/assets}/demo/flags/flags.scss +0 -0
  80. /package/{assets → src/assets}/demo/flags/flags_responsive.png +0 -0
  81. /package/{assets → src/assets}/demo/images/access/asset-access.svg +0 -0
  82. /package/{assets → src/assets}/demo/images/error/asset-error.svg +0 -0
  83. /package/{assets → src/assets}/demo/images/flag/flag_placeholder.png +0 -0
  84. /package/{assets → src/assets}/favicon.svg +0 -0
  85. /package/{assets → src/assets}/i18n/app-modules.en.json +0 -0
  86. /package/{assets → src/assets}/i18n/app-modules.ru.json +0 -0
  87. /package/{assets → src/assets}/i18n/config.en.json +0 -0
  88. /package/{assets → src/assets}/i18n/config.ru.json +0 -0
  89. /package/{assets → src/assets}/layout/_core.scss +0 -0
  90. /package/{assets → src/assets}/layout/_footer.scss +0 -0
  91. /package/{assets → src/assets}/layout/_logo.scss +0 -0
  92. /package/{assets → src/assets}/layout/_main.scss +0 -0
  93. /package/{assets → src/assets}/layout/_menu.scss +0 -0
  94. /package/{assets → src/assets}/layout/_mixins.scss +0 -0
  95. /package/{assets → src/assets}/layout/_preloading.scss +0 -0
  96. /package/{assets → src/assets}/layout/_responsive.scss +0 -0
  97. /package/{assets → src/assets}/layout/_topbar.scss +0 -0
  98. /package/{assets → src/assets}/layout/_typography.scss +0 -0
  99. /package/{assets → src/assets}/layout/_utils.scss +0 -0
  100. /package/{assets → src/assets}/layout/layout.scss +0 -0
  101. /package/{assets → src/assets}/layout/variables/_common.scss +0 -0
  102. /package/{assets → src/assets}/layout/variables/_dark.scss +0 -0
  103. /package/{assets → src/assets}/layout/variables/_light.scss +0 -0
  104. /package/{assets → src/assets}/oip-common.scss +0 -0
  105. /package/{assets → src/assets}/tailwind.css +0 -0
@@ -0,0 +1,491 @@
1
+ import { CommonModule, isPlatformBrowser } from '@angular/common';
2
+ import { Component, computed, inject, PLATFORM_ID, signal, OnInit } from '@angular/core';
3
+ import { FormsModule } from '@angular/forms';
4
+ import { Router } from '@angular/router';
5
+ import { $t, updatePreset, updateSurfacePalette } from '@primeng/themes';
6
+ import Aura from '@primeng/themes/aura';
7
+ import Lara from '@primeng/themes/lara';
8
+ import Nora from '@primeng/themes/nora';
9
+ import { PrimeNG } from 'primeng/config';
10
+ import { SelectButtonModule } from 'primeng/selectbutton';
11
+ import { LayoutService } from '../services/app.layout.service';
12
+
13
+ const presets = {
14
+ Aura,
15
+ Lara,
16
+ Nora
17
+ } as const;
18
+
19
+ declare type KeyOfType<T> = keyof T extends infer U ? U : never;
20
+
21
+ declare type SurfacesType = {
22
+ name?: string;
23
+ palette?: {
24
+ 0?: string;
25
+ 50?: string;
26
+ 100?: string;
27
+ 200?: string;
28
+ 300?: string;
29
+ 400?: string;
30
+ 500?: string;
31
+ 600?: string;
32
+ 700?: string;
33
+ 800?: string;
34
+ 900?: string;
35
+ 950?: string;
36
+ };
37
+ };
38
+
39
+ @Component({
40
+ selector: 'app-configurator',
41
+ standalone: true,
42
+ imports: [CommonModule, FormsModule, SelectButtonModule],
43
+ template: `
44
+ <div class="flex flex-col gap-4">
45
+ <div>
46
+ <span class="text-sm text-muted-color font-semibold">Primary</span>
47
+ <div class="pt-2 flex gap-2 flex-wrap justify-start">
48
+ @for (primaryColor of primaryColors(); track primaryColor.name) {
49
+ <button
50
+ class="border-none w-5 h-5 rounded-full p-0 cursor-pointer outline-none outline-offset-1"
51
+ id="oip-app-configurator-primary-color-{{ primaryColor.name }}"
52
+ type="button"
53
+ [ngClass]="{
54
+ 'outline-primary': primaryColor.name === selectedPrimaryColor()
55
+ }"
56
+ [style]="{
57
+ 'background-color': primaryColor?.name === 'noir' ? 'var(--text-color)' : primaryColor?.palette?.['500']
58
+ }"
59
+ [title]="primaryColor.name"
60
+ (click)="updateColors($event, 'primary', primaryColor)"></button>
61
+ }
62
+ </div>
63
+ </div>
64
+ <div>
65
+ <span class="text-sm text-muted-color font-semibold">Surface</span>
66
+ <div class="pt-2 flex gap-2 flex-wrap justify-start">
67
+ @for (surface of surfaces; track surface.name) {
68
+ <button
69
+ class="border-none w-5 h-5 rounded-full p-0 cursor-pointer outline-none outline-offset-1"
70
+ id="oip-app-configurator-surface-color-{{ surface.name }}"
71
+ type="button"
72
+ [ngClass]="{
73
+ 'outline-primary': selectedSurfaceColor()
74
+ ? selectedSurfaceColor() === surface.name
75
+ : layoutService.layoutConfig().darkTheme
76
+ ? surface.name === 'zinc'
77
+ : surface.name === 'slate'
78
+ }"
79
+ [style]="{
80
+ 'background-color': surface?.name === 'noir' ? 'var(--text-color)' : surface?.palette?.['500']
81
+ }"
82
+ [title]="surface.name"
83
+ (click)="updateColors($event, 'surface', surface)"></button>
84
+ }
85
+ </div>
86
+ </div>
87
+ <div class="flex flex-col gap-2">
88
+ <span class="text-sm text-muted-color font-semibold">Presets</span>
89
+ <p-selectButton
90
+ id="oip-app-configurator-preset-select-button"
91
+ size="small"
92
+ [allowEmpty]="false"
93
+ [ngModel]="selectedPreset()"
94
+ [options]="presets"
95
+ (ngModelChange)="onPresetChange($event)" />
96
+ </div>
97
+ @if (showMenuModeButton()) {
98
+ <div class="flex flex-col gap-2">
99
+ <span class="text-sm text-muted-color font-semibold">Menu Mode</span>
100
+ <p-selectButton
101
+ id="oip-app-configurator-menu-mode-select-button"
102
+ size="small"
103
+ [allowEmpty]="false"
104
+ [ngModel]="menuMode()"
105
+ [options]="menuModeOptions"
106
+ (ngModelChange)="onMenuModeChange($event)" />
107
+ </div>
108
+ }
109
+ </div>
110
+ `,
111
+ host: {
112
+ class:
113
+ 'hidden absolute top-[3.25rem] right-0 w-72 p-4 bg-surface-0 dark:bg-surface-900 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)]'
114
+ }
115
+ })
116
+ export class AppConfiguratorComponent implements OnInit {
117
+ router = inject(Router);
118
+
119
+ config: PrimeNG = inject(PrimeNG);
120
+
121
+ layoutService: LayoutService = inject(LayoutService);
122
+
123
+ platformId = inject(PLATFORM_ID);
124
+
125
+ primeng = inject(PrimeNG);
126
+
127
+ presets = Object.keys(presets);
128
+
129
+ showMenuModeButton = signal(!this.router.url.includes('auth'));
130
+
131
+ menuModeOptions = [
132
+ { label: 'Static', value: 'static' },
133
+ { label: 'Overlay', value: 'overlay' }
134
+ ];
135
+
136
+ ngOnInit() {
137
+ if (isPlatformBrowser(this.platformId)) {
138
+ this.onPresetChange(this.layoutService.layoutConfig().preset);
139
+ }
140
+ }
141
+
142
+ surfaces: SurfacesType[] = [
143
+ {
144
+ name: 'slate',
145
+ palette: {
146
+ 0: '#ffffff',
147
+ 50: '#f8fafc',
148
+ 100: '#f1f5f9',
149
+ 200: '#e2e8f0',
150
+ 300: '#cbd5e1',
151
+ 400: '#94a3b8',
152
+ 500: '#64748b',
153
+ 600: '#475569',
154
+ 700: '#334155',
155
+ 800: '#1e293b',
156
+ 900: '#0f172a',
157
+ 950: '#020617'
158
+ }
159
+ },
160
+ {
161
+ name: 'gray',
162
+ palette: {
163
+ 0: '#ffffff',
164
+ 50: '#f9fafb',
165
+ 100: '#f3f4f6',
166
+ 200: '#e5e7eb',
167
+ 300: '#d1d5db',
168
+ 400: '#9ca3af',
169
+ 500: '#6b7280',
170
+ 600: '#4b5563',
171
+ 700: '#374151',
172
+ 800: '#1f2937',
173
+ 900: '#111827',
174
+ 950: '#030712'
175
+ }
176
+ },
177
+ {
178
+ name: 'zinc',
179
+ palette: {
180
+ 0: '#ffffff',
181
+ 50: '#fafafa',
182
+ 100: '#f4f4f5',
183
+ 200: '#e4e4e7',
184
+ 300: '#d4d4d8',
185
+ 400: '#a1a1aa',
186
+ 500: '#71717a',
187
+ 600: '#52525b',
188
+ 700: '#3f3f46',
189
+ 800: '#27272a',
190
+ 900: '#18181b',
191
+ 950: '#09090b'
192
+ }
193
+ },
194
+ {
195
+ name: 'neutral',
196
+ palette: {
197
+ 0: '#ffffff',
198
+ 50: '#fafafa',
199
+ 100: '#f5f5f5',
200
+ 200: '#e5e5e5',
201
+ 300: '#d4d4d4',
202
+ 400: '#a3a3a3',
203
+ 500: '#737373',
204
+ 600: '#525252',
205
+ 700: '#404040',
206
+ 800: '#262626',
207
+ 900: '#171717',
208
+ 950: '#0a0a0a'
209
+ }
210
+ },
211
+ {
212
+ name: 'stone',
213
+ palette: {
214
+ 0: '#ffffff',
215
+ 50: '#fafaf9',
216
+ 100: '#f5f5f4',
217
+ 200: '#e7e5e4',
218
+ 300: '#d6d3d1',
219
+ 400: '#a8a29e',
220
+ 500: '#78716c',
221
+ 600: '#57534e',
222
+ 700: '#44403c',
223
+ 800: '#292524',
224
+ 900: '#1c1917',
225
+ 950: '#0c0a09'
226
+ }
227
+ },
228
+ {
229
+ name: 'soho',
230
+ palette: {
231
+ 0: '#ffffff',
232
+ 50: '#ececec',
233
+ 100: '#dedfdf',
234
+ 200: '#c4c4c6',
235
+ 300: '#adaeb0',
236
+ 400: '#97979b',
237
+ 500: '#7f8084',
238
+ 600: '#6a6b70',
239
+ 700: '#55565b',
240
+ 800: '#3f4046',
241
+ 900: '#2c2c34',
242
+ 950: '#16161d'
243
+ }
244
+ },
245
+ {
246
+ name: 'viva',
247
+ palette: {
248
+ 0: '#ffffff',
249
+ 50: '#f3f3f3',
250
+ 100: '#e7e7e8',
251
+ 200: '#cfd0d0',
252
+ 300: '#b7b8b9',
253
+ 400: '#9fa1a1',
254
+ 500: '#87898a',
255
+ 600: '#6e7173',
256
+ 700: '#565a5b',
257
+ 800: '#3e4244',
258
+ 900: '#262b2c',
259
+ 950: '#0e1315'
260
+ }
261
+ },
262
+ {
263
+ name: 'ocean',
264
+ palette: {
265
+ 0: '#ffffff',
266
+ 50: '#fbfcfc',
267
+ 100: '#F7F9F8',
268
+ 200: '#EFF3F2',
269
+ 300: '#DADEDD',
270
+ 400: '#B1B7B6',
271
+ 500: '#828787',
272
+ 600: '#5F7274',
273
+ 700: '#415B61',
274
+ 800: '#29444E',
275
+ 900: '#183240',
276
+ 950: '#0c1920'
277
+ }
278
+ }
279
+ ];
280
+
281
+ selectedPrimaryColor = computed(() => {
282
+ return this.layoutService.layoutConfig().primary;
283
+ });
284
+
285
+ selectedSurfaceColor = computed(() => this.layoutService.layoutConfig().surface);
286
+
287
+ selectedPreset = computed(() => this.layoutService.layoutConfig().preset);
288
+
289
+ menuMode = computed(() => this.layoutService.layoutConfig().menuMode);
290
+
291
+ primaryColors = computed<SurfacesType[]>(() => {
292
+ const presetPalette = presets[this.layoutService.layoutConfig().preset as KeyOfType<typeof presets>].primitive;
293
+ const colors = [
294
+ 'emerald',
295
+ 'green',
296
+ 'lime',
297
+ 'orange',
298
+ 'amber',
299
+ 'yellow',
300
+ 'teal',
301
+ 'cyan',
302
+ 'sky',
303
+ 'blue',
304
+ 'indigo',
305
+ 'violet',
306
+ 'purple',
307
+ 'fuchsia',
308
+ 'pink',
309
+ 'rose'
310
+ ];
311
+ const palettes: SurfacesType[] = [{ name: 'noir', palette: {} }];
312
+
313
+ colors.forEach((color) => {
314
+ palettes.push({
315
+ name: color,
316
+ palette: presetPalette?.[color as KeyOfType<typeof presetPalette>] as SurfacesType['palette']
317
+ });
318
+ });
319
+
320
+ return palettes;
321
+ });
322
+
323
+ getPresetExt() {
324
+ const color: SurfacesType = this.primaryColors().find((c) => c.name === this.selectedPrimaryColor()) || {};
325
+ const preset = this.layoutService.layoutConfig().preset;
326
+
327
+ if (color.name === 'noir') {
328
+ return {
329
+ semantic: {
330
+ primary: {
331
+ 50: '{surface.50}',
332
+ 100: '{surface.100}',
333
+ 200: '{surface.200}',
334
+ 300: '{surface.300}',
335
+ 400: '{surface.400}',
336
+ 500: '{surface.500}',
337
+ 600: '{surface.600}',
338
+ 700: '{surface.700}',
339
+ 800: '{surface.800}',
340
+ 900: '{surface.900}',
341
+ 950: '{surface.950}'
342
+ },
343
+ colorScheme: {
344
+ light: {
345
+ primary: {
346
+ color: '{primary.950}',
347
+ contrastColor: '#ffffff',
348
+ hoverColor: '{primary.800}',
349
+ activeColor: '{primary.700}'
350
+ },
351
+ highlight: {
352
+ background: '{primary.950}',
353
+ focusBackground: '{primary.700}',
354
+ color: '#ffffff',
355
+ focusColor: '#ffffff'
356
+ }
357
+ },
358
+ dark: {
359
+ primary: {
360
+ color: '{primary.50}',
361
+ contrastColor: '{primary.950}',
362
+ hoverColor: '{primary.200}',
363
+ activeColor: '{primary.300}'
364
+ },
365
+ highlight: {
366
+ background: '{primary.50}',
367
+ focusBackground: '{primary.300}',
368
+ color: '{primary.950}',
369
+ focusColor: '{primary.950}'
370
+ }
371
+ }
372
+ }
373
+ }
374
+ };
375
+ } else if (preset === 'Nora') {
376
+ return {
377
+ semantic: {
378
+ primary: color.palette,
379
+ colorScheme: {
380
+ light: {
381
+ primary: {
382
+ color: '{primary.600}',
383
+ contrastColor: '#ffffff',
384
+ hoverColor: '{primary.700}',
385
+ activeColor: '{primary.800}'
386
+ },
387
+ highlight: {
388
+ background: '{primary.600}',
389
+ focusBackground: '{primary.700}',
390
+ color: '#ffffff',
391
+ focusColor: '#ffffff'
392
+ }
393
+ },
394
+ dark: {
395
+ primary: {
396
+ color: '{primary.500}',
397
+ contrastColor: '{surface.900}',
398
+ hoverColor: '{primary.400}',
399
+ activeColor: '{primary.300}'
400
+ },
401
+ highlight: {
402
+ background: '{primary.500}',
403
+ focusBackground: '{primary.400}',
404
+ color: '{surface.900}',
405
+ focusColor: '{surface.900}'
406
+ }
407
+ }
408
+ }
409
+ }
410
+ };
411
+ } else {
412
+ return {
413
+ semantic: {
414
+ primary: color.palette,
415
+ colorScheme: {
416
+ light: {
417
+ primary: {
418
+ color: '{primary.500}',
419
+ contrastColor: '#ffffff',
420
+ hoverColor: '{primary.600}',
421
+ activeColor: '{primary.700}'
422
+ },
423
+ highlight: {
424
+ background: '{primary.50}',
425
+ focusBackground: '{primary.100}',
426
+ color: '{primary.700}',
427
+ focusColor: '{primary.800}'
428
+ }
429
+ },
430
+ dark: {
431
+ primary: {
432
+ color: '{primary.400}',
433
+ contrastColor: '{surface.900}',
434
+ hoverColor: '{primary.300}',
435
+ activeColor: '{primary.200}'
436
+ },
437
+ highlight: {
438
+ background: 'color-mix(in srgb, {primary.400}, transparent 84%)',
439
+ focusBackground: 'color-mix(in srgb, {primary.400}, transparent 76%)',
440
+ color: 'rgba(255,255,255,.87)',
441
+ focusColor: 'rgba(255,255,255,.87)'
442
+ }
443
+ }
444
+ }
445
+ }
446
+ };
447
+ }
448
+ }
449
+
450
+ updateColors(event: MouseEvent, type: string, color: SurfacesType) {
451
+ if (type === 'primary') {
452
+ this.layoutService.layoutConfig.update((state) => ({
453
+ ...state,
454
+ primary: color.name
455
+ }));
456
+ } else if (type === 'surface') {
457
+ this.layoutService.layoutConfig.update((state) => ({
458
+ ...state,
459
+ surface: color.name
460
+ }));
461
+ }
462
+ this.applyTheme(type, color);
463
+
464
+ event.stopPropagation();
465
+ }
466
+
467
+ applyTheme(type: string, color: SurfacesType) {
468
+ if (type === 'primary') {
469
+ updatePreset(this.getPresetExt());
470
+ } else if (type === 'surface') {
471
+ updateSurfacePalette(color.palette);
472
+ }
473
+ }
474
+
475
+ onPresetChange(event: string) {
476
+ this.layoutService.layoutConfig.update((state) => ({
477
+ ...state,
478
+ preset: event
479
+ }));
480
+ const preset = presets[event as KeyOfType<typeof presets>];
481
+ const surfacePalette = this.surfaces.find((s) => s.name === this.selectedSurfaceColor())?.palette;
482
+ $t().preset(preset).preset(this.getPresetExt()).surfacePalette(surfacePalette).use({ useDefaultOptions: true });
483
+ }
484
+
485
+ onMenuModeChange(event: string) {
486
+ this.layoutService.layoutConfig.update((prev) => ({
487
+ ...prev,
488
+ menuMode: event
489
+ }));
490
+ }
491
+ }
@@ -0,0 +1,47 @@
1
+ import { Component, computed, inject } from '@angular/core';
2
+ import { ButtonModule } from 'primeng/button';
3
+ import { StyleClassModule } from 'primeng/styleclass';
4
+ import { AppConfiguratorComponent } from './app-configurator.component';
5
+ import { LayoutService } from '../services/app.layout.service';
6
+
7
+ @Component({
8
+ selector: 'app-floating-configurator',
9
+ imports: [ButtonModule, StyleClassModule, AppConfiguratorComponent],
10
+ template: `
11
+ <div class="fixed flex gap-4 top-8 right-8">
12
+ <p-button
13
+ id="oip-app-floating-configurator-dark-theme-button"
14
+ severity="secondary"
15
+ type="button"
16
+ [icon]="isDarkTheme() ? 'pi pi-moon' : 'pi pi-sun'"
17
+ [rounded]="true"
18
+ (onClick)="toggleDarkMode()" />
19
+ <div class="relative">
20
+ <p-button
21
+ enterActiveClass="animate-scalein"
22
+ enterFromClass="hidden"
23
+ icon="pi pi-palette"
24
+ id="oip-app-floating-configurator-next-button"
25
+ leaveActiveClass="animate-fadeout"
26
+ leaveToClass="hidden"
27
+ pStyleClass="@next"
28
+ type="button"
29
+ [hideOnOutsideClick]="true"
30
+ [rounded]="true" />
31
+ <app-configurator />
32
+ </div>
33
+ </div>
34
+ `
35
+ })
36
+ export class AppFloatingConfiguratorComponent {
37
+ LayoutService = inject(LayoutService);
38
+
39
+ isDarkTheme = computed(() => this.LayoutService.layoutConfig().darkTheme);
40
+
41
+ toggleDarkMode() {
42
+ this.LayoutService.layoutConfig.update((state) => ({
43
+ ...state,
44
+ darkTheme: !state.darkTheme
45
+ }));
46
+ }
47
+ }
@@ -0,0 +1,144 @@
1
+ import { Component, inject, OnInit } from '@angular/core';
2
+ import { Tooltip } from 'primeng/tooltip';
3
+ import { FormsModule } from '@angular/forms';
4
+ import { TableModule } from 'primeng/table';
5
+ import { BaseDataService } from '../services/base-data.service';
6
+ import { Tag } from 'primeng/tag';
7
+ import { ButtonModule } from 'primeng/button';
8
+ import { ToolbarModule } from 'primeng/toolbar';
9
+ import { MsgService } from '../services/msg.service';
10
+ import { ConfirmationService } from 'primeng/api';
11
+ import { ConfirmDialog } from 'primeng/confirmdialog';
12
+ import { L10nService } from '../services/l10n.service';
13
+ import { Module } from '../api/Module';
14
+ import { ModuleDto } from '../api/data-contracts';
15
+ import { AppTitleService } from '../services/app-title.service';
16
+
17
+ interface L10n {
18
+ confirm: {
19
+ header: string;
20
+ message: string;
21
+ cancel: string;
22
+ delete: string;
23
+ };
24
+ title: string;
25
+ messages: {
26
+ deleteSuccess: string;
27
+ };
28
+ table: {
29
+ deleteTooltip: string;
30
+ currentlyLoaded: string;
31
+ yes: string;
32
+ no: string;
33
+ name: string;
34
+ moduleId: string;
35
+ };
36
+ refreshTooltip: string;
37
+ }
38
+
39
+ @Component({
40
+ imports: [FormsModule, TableModule, Tag, ButtonModule, ToolbarModule, Tooltip, ConfirmDialog],
41
+ providers: [ConfirmationService, Module],
42
+ selector: 'app-modules',
43
+ template: `
44
+ <p-confirmDialog></p-confirmDialog>
45
+ <div class="flex flex-col md:flex-row gap-4">
46
+ <div class="card w-full">
47
+ <div class="font-semibold text-xl mb-4">
48
+ {{ l10n.title }}
49
+ </div>
50
+ <div class="mb-4">
51
+ <p-toolbar>
52
+ <p-button
53
+ icon="pi pi-refresh"
54
+ rounded="true"
55
+ severity="secondary"
56
+ text="true"
57
+ tooltipPosition="bottom"
58
+ [pTooltip]="l10n.refreshTooltip"
59
+ (onClick)="refreshAction()"></p-button>
60
+ </p-toolbar>
61
+ </div>
62
+ <p-table class="mt-4" [paginator]="true" [rows]="100" [value]="modules">
63
+ <ng-template pTemplate="header">
64
+ <tr>
65
+ <th>{{ l10n.table.moduleId }}</th>
66
+ <th>{{ l10n.table.name }}</th>
67
+ <th>{{ l10n.table.currentlyLoaded }}</th>
68
+ <th style="width: 4rem"></th>
69
+ </tr>
70
+ </ng-template>
71
+ <ng-template let-module pTemplate="body">
72
+ <tr>
73
+ <td>{{ module.moduleId }}</td>
74
+ <td>{{ module.name }}</td>
75
+ <td>
76
+ <p-tag
77
+ [severity]="module.currentlyLoaded ? 'success' : 'danger'"
78
+ [value]="module.currentlyLoaded ? l10n.table.yes : l10n.table.no"></p-tag>
79
+ </td>
80
+ <td>
81
+ <p-button
82
+ icon="pi pi-trash"
83
+ rounded="true"
84
+ severity="danger"
85
+ text="true"
86
+ tooltipPosition="bottom"
87
+ [pTooltip]="l10n.table.deleteTooltip"
88
+ (onClick)="deleteModule(module)"></p-button>
89
+ </td>
90
+ </tr>
91
+ </ng-template>
92
+ </p-table>
93
+ </div>
94
+ </div>
95
+ `
96
+ })
97
+ export class AppModulesComponent implements OnInit {
98
+ protected dataService = inject(BaseDataService);
99
+ protected modules: ModuleDto[] = [];
100
+ protected msgService = inject(MsgService);
101
+ protected confirmationService = inject(ConfirmationService);
102
+ protected l10nService = inject(L10nService);
103
+ protected l10n: L10n = {} as L10n;
104
+ protected titleService = inject(AppTitleService);
105
+ private moduleService = inject(Module);
106
+
107
+ async ngOnInit() {
108
+ (await this.l10nService.get('app-modules')).subscribe((l) => {
109
+ this.l10n = l;
110
+ });
111
+ this.titleService.setTitle(this.l10n.title);
112
+ await this.refreshAction();
113
+ }
114
+
115
+ async refreshAction() {
116
+ this.modules = await this.moduleService.moduleGetModulesWithLoadStatus();
117
+ }
118
+
119
+ deleteModule(module: ModuleDto) {
120
+ this.confirmationService.confirm({
121
+ header: this.l10n.confirm.header,
122
+ message: this.l10n.confirm.message,
123
+ icon: 'pi pi-trash',
124
+ rejectButtonProps: {
125
+ label: this.l10n.confirm.cancel,
126
+ severity: 'secondary',
127
+ outlined: true
128
+ },
129
+ acceptButtonProps: {
130
+ label: this.l10n.confirm.delete,
131
+ severity: 'danger'
132
+ },
133
+ accept: async () => {
134
+ this.dataService
135
+ .sendRequest(`api/module/delete`, 'DELETE', {
136
+ moduleId: module.moduleId
137
+ })
138
+ .then(() => this.refreshAction())
139
+ .catch((error) => console.error(error));
140
+ this.msgService.success(this.l10n.messages.deleteSuccess);
141
+ }
142
+ });
143
+ }
144
+ }