@yuuvis/app-shell-admin 2.0.0-beta.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.
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # feature-admin
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Running unit tests
6
+
7
+ Run `nx test feature-admin` to execute the unit tests.
@@ -0,0 +1,421 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { inject, Injectable, signal, Component } from '@angular/core';
4
+ import * as i1 from '@angular/forms';
5
+ import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
6
+ import * as i5 from '@angular/material/button';
7
+ import { MatButtonModule } from '@angular/material/button';
8
+ import { MatCardModule } from '@angular/material/card';
9
+ import * as i6 from '@angular/material/checkbox';
10
+ import { MatCheckboxModule } from '@angular/material/checkbox';
11
+ import * as i3 from '@angular/material/form-field';
12
+ import { MatFormFieldModule } from '@angular/material/form-field';
13
+ import * as i7 from '@angular/material/icon';
14
+ import { MatIconModule } from '@angular/material/icon';
15
+ import * as i8 from '@angular/material/input';
16
+ import { MatInputModule } from '@angular/material/input';
17
+ import * as i4 from '@ngx-translate/core';
18
+ import { TranslateModule } from '@ngx-translate/core';
19
+ import { BackendService, YUV_USER, SystemType, TranslateModule as TranslateModule$1 } from '@yuuvis/client-core';
20
+ import * as i9 from '@yuuvis/client-framework/autocomplete';
21
+ import { YuvAutocompleteModule } from '@yuuvis/client-framework/autocomplete';
22
+ import { YuvFormsModule } from '@yuuvis/client-framework/forms';
23
+ import * as i2 from '@yuuvis/client-framework/split-view';
24
+ import { YuvSplitViewModule } from '@yuuvis/client-framework/split-view';
25
+ import { FeatureService, ShellService, ShellConfigService, SHELL_CONFIG_TYPES } from '@yuuvis/client-shell-core';
26
+ import { map, finalize, tap, switchMap, of } from 'rxjs';
27
+ import { MatProgressBar } from '@angular/material/progress-bar';
28
+ import * as i10 from '@angular/material/tooltip';
29
+ import { MatTooltipModule } from '@angular/material/tooltip';
30
+ import * as i2$1 from '@angular/material/radio';
31
+ import { MatRadioModule } from '@angular/material/radio';
32
+ import * as i1$1 from '@angular/router';
33
+ import { RouterModule } from '@angular/router';
34
+
35
+ class AppsService {
36
+ #backend = inject(BackendService);
37
+ getUsers() {
38
+ return this.#backend.get(`/tenant-management/api/admin/users`, '').pipe(map((response) => {
39
+ return response.map((user) => {
40
+ const { id, username, createdTimestamp, email, enabled, firstName, lastName, groups, roles } = user;
41
+ return { id, username, createdTimestamp, email, enabled, firstName, lastName, groups, roles };
42
+ });
43
+ }));
44
+ }
45
+ getRoles() {
46
+ return this.#backend.get(`/tenant-management/api/admin/roles`, '');
47
+ }
48
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: AppsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
49
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: AppsService, providedIn: 'root' });
50
+ }
51
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: AppsService, decorators: [{
52
+ type: Injectable,
53
+ args: [{
54
+ providedIn: 'root'
55
+ }]
56
+ }] });
57
+
58
+ class AppsPageComponent {
59
+ #appsService = inject(AppsService);
60
+ #features = inject(FeatureService);
61
+ #fb = inject(FormBuilder);
62
+ #shell = inject(ShellService);
63
+ // user token has to be present because this is a part of the
64
+ // required bootstrap process
65
+ user = inject(YUV_USER);
66
+ // only admins should be able to see this page
67
+ isValidUser = this.user?.authorities.includes('YUUVIS_TENANT_ADMIN');
68
+ users = [];
69
+ mappedUsers = [];
70
+ roles = [];
71
+ features = [];
72
+ selectedUser;
73
+ autocompleteValues = [];
74
+ busy = signal(false);
75
+ loadingUsers = signal(false);
76
+ featuresForm = this.#fb.group({
77
+ features: this.#fb.array([])
78
+ });
79
+ get featureArray() {
80
+ return this.featuresForm.controls['features'];
81
+ }
82
+ getFeatureForm(c) {
83
+ return c;
84
+ }
85
+ hasFeatureConfig = this.#features.hasFeatureConfig();
86
+ constructor() {
87
+ this.#fetchRoles();
88
+ this.#fetchUsers();
89
+ // this.features = this.#features.getFeatures();
90
+ this.featuresForm.valueChanges.subscribe((v) => {
91
+ this.#mapUsersToFeatures();
92
+ });
93
+ // this.features.forEach((feature) => {
94
+ // this.featureArray.push(
95
+ // this.#fb.group({
96
+ // id: [feature.id, Validators.required],
97
+ // label: [feature.label],
98
+ // extension: [feature.extension],
99
+ // allowed: [(feature.allowed || []).map((a) => ({ label: a, value: a }))],
100
+ // denied: [(feature.denied || []).map((d) => ({ label: d, value: d }))]
101
+ // })
102
+ // );
103
+ // });
104
+ this.#loadFeatures();
105
+ }
106
+ #loadFeatures() {
107
+ this.features = this.#features.getFeatures();
108
+ this.featuresForm.patchValue({
109
+ features: []
110
+ });
111
+ this.features.forEach((feature) => {
112
+ this.featureArray.push(this.#fb.group({
113
+ id: [feature.id, Validators.required],
114
+ label: [feature.label],
115
+ extension: [feature.extension],
116
+ allowed: [(feature.allowed || []).map((a) => ({ label: a, value: a }))],
117
+ denied: [(feature.denied || []).map((d) => ({ label: d, value: d }))]
118
+ }));
119
+ });
120
+ }
121
+ createFeatureConfigObject() {
122
+ this.busy.set(true);
123
+ this.#features
124
+ .createSettingsObject()
125
+ .pipe(finalize(() => {
126
+ this.busy.set(false);
127
+ }))
128
+ .subscribe(() => {
129
+ this.hasFeatureConfig = this.#features.hasFeatureConfig();
130
+ });
131
+ }
132
+ #fetchUsers() {
133
+ this.loadingUsers.set(true);
134
+ this.#appsService
135
+ .getUsers()
136
+ .pipe(finalize(() => {
137
+ this.loadingUsers.set(false);
138
+ }))
139
+ .subscribe((users) => {
140
+ this.users = users;
141
+ this.#mapUsersToFeatures();
142
+ });
143
+ }
144
+ #mapUsersToFeatures() {
145
+ // map form to features
146
+ const features = this.#formToFeatures();
147
+ this.mappedUsers = this.users.map((user) => {
148
+ return {
149
+ user,
150
+ features: features.filter((f) => this.#features.canUseFeature(f, user.roles))
151
+ };
152
+ });
153
+ }
154
+ #formToFeatures() {
155
+ return this.featureArray.value.map((f) => {
156
+ return {
157
+ id: f.id,
158
+ label: f.label,
159
+ extension: f.extension,
160
+ allowed: f.allowed.map((a) => a.value),
161
+ denied: f.denied.map((d) => d.value)
162
+ };
163
+ });
164
+ }
165
+ #fetchRoles() {
166
+ this.#appsService.getRoles().subscribe((roles) => {
167
+ this.roles = roles;
168
+ this.autocompleteValues = roles.map((role) => ({
169
+ label: role.name,
170
+ value: role.name
171
+ }));
172
+ });
173
+ }
174
+ addFeature() {
175
+ this.featureArray.push(this.#fb.group({
176
+ id: ['', Validators.required],
177
+ label: [''],
178
+ extension: [false],
179
+ allowed: [[]],
180
+ denied: [[]]
181
+ }));
182
+ }
183
+ removeFeature(idx) {
184
+ this.featureArray.removeAt(idx);
185
+ this.featuresForm.markAsDirty();
186
+ }
187
+ fetchRoles(t) {
188
+ this.autocompleteValues = this.roles
189
+ .filter((role) => role.name.toLowerCase().includes(t.toLowerCase()))
190
+ .map((role) => ({
191
+ label: role.name,
192
+ value: role.name
193
+ }));
194
+ }
195
+ delete() {
196
+ this.busy.set(true);
197
+ this.#features
198
+ .deleteFeatureSettings()
199
+ .pipe(finalize(() => {
200
+ this.busy.set(false);
201
+ }))
202
+ .subscribe(() => {
203
+ this.featureArray.clear();
204
+ this.features = [];
205
+ this.hasFeatureConfig = false;
206
+ this.featuresForm.markAsPristine();
207
+ });
208
+ }
209
+ save() {
210
+ this.busy.set(true);
211
+ const features = this.#formToFeatures();
212
+ this.#features
213
+ .saveFeatureSettings(features)
214
+ .pipe(finalize(() => {
215
+ this.busy.set(false);
216
+ }))
217
+ .subscribe(() => this.featuresForm.markAsPristine());
218
+ }
219
+ export() {
220
+ this.#features.export();
221
+ }
222
+ import() {
223
+ this.#features.import().subscribe();
224
+ }
225
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: AppsPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
226
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.1", type: AppsPageComponent, isStandalone: true, selector: "ysfa-apps", ngImport: i0, template: "<yuv-split-view [gutterSize]=\"32\">\n <!-- features -->\n <ng-template yuvSplitArea [size]=\"60\">\n <section class=\"features\">\n <header>\n <h2>\n {{ 'yuv.app.shell-admin.features.headline' | translate }}\n </h2>\n\n @if (hasFeatureConfig) {\n <div class=\"export\">\n <button [disabled]=\"busy()\" mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.config.export.tooltip' | translate\" (click)=\"export()\">\n <mat-icon>download</mat-icon>\n </button>\n <button [disabled]=\"busy()\" mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.config.import.tooltip' | translate\" (click)=\"import()\">\n <mat-icon>upload</mat-icon>\n </button>\n </div>\n\n <div class=\"buttons\">\n <button mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.features.config.add.tooltip' | translate\" [disabled]=\"busy()\" (click)=\"addFeature()\"><mat-icon>add</mat-icon></button>\n <button [disabled]=\"featuresForm.invalid || featuresForm.pristine || busy()\" mat-flat-button (click)=\"save()\">\n {{ 'yuv.app.shell-admin.features.config.save' | translate }}\n </button>\n <button mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.config.delete.tooltip' | translate\" [disabled]=\"busy()\" (click)=\"delete()\"><mat-icon>delete</mat-icon></button>\n </div>\n } @else {\n <button mat-flat-button [disabled]=\"busy()\" (click)=\"createFeatureConfigObject()\">\n {{ 'yuv.app.shell-admin.features.config.create' | translate }}\n </button>\n }\n\n @if (busy()) {\n <mat-progress-bar mode=\"indeterminate\" class=\"progress-bar\"></mat-progress-bar>\n }\n </header>\n\n <form class=\"main\" [formGroup]=\"featuresForm\">\n <div formArrayName=\"features\">\n @for (fForm of featureArray.controls; track $index) {\n <section class=\"feature\" [formGroup]=\"getFeatureForm(fForm)\">\n <button mat-icon-button (click)=\"removeFeature($index)\"><mat-icon>delete</mat-icon></button>\n\n <div class=\"ext\">\n <mat-checkbox formControlName=\"extension\">{{ 'yuv.app.shell-admin.features.config.feature.extension' | translate }}</mat-checkbox>\n </div>\n\n <mat-form-field class=\"id\">\n <mat-label>{{ 'yuv.app.shell-admin.features.config.feature.id' | translate }}</mat-label>\n <input matInput formControlName=\"id\" />\n </mat-form-field>\n\n <mat-form-field class=\"label\">\n <mat-label>{{ 'yuv.app.shell-admin.features.config.feature.label' | translate }}</mat-label>\n <input matInput formControlName=\"label\" />\n </mat-form-field>\n\n <mat-form-field class=\"allowed\">\n <mat-label>{{ 'yuv.app.shell-admin.features.config.feature.allowed' | translate }}</mat-label>\n <yuv-autocomplete\n formControlName=\"allowed\"\n [multiple]=\"true\"\n [forceSelection]=\"true\"\n [distinctValues]=\"true\"\n (autocompleteFnc)=\"fetchRoles($event)\"\n [autocompleteValues]=\"autocompleteValues\"\n ></yuv-autocomplete>\n </mat-form-field>\n\n <mat-form-field class=\"denied\">\n <mat-label>{{ 'yuv.app.shell-admin.features.config.feature.denied' | translate }}</mat-label>\n <yuv-autocomplete\n formControlName=\"denied\"\n [multiple]=\"true\"\n [forceSelection]=\"true\"\n [distinctValues]=\"true\"\n (autocompleteFnc)=\"fetchRoles($event)\"\n [autocompleteValues]=\"autocompleteValues\"\n ></yuv-autocomplete>\n </mat-form-field>\n </section>\n }\n </div>\n </form>\n </section>\n </ng-template>\n\n <!-- users -->\n <ng-template yuvSplitArea [size]=\"40\">\n <section class=\"users\">\n <header>\n <h2>\n {{ 'yuv.app.shell-admin.features.users.headline' | translate }}\n </h2>\n @if (loadingUsers()) {\n <mat-progress-bar mode=\"indeterminate\" class=\"progress-bar\"></mat-progress-bar>\n }\n </header>\n <main>\n @for (u of mappedUsers; track u.user.id) {\n <div (click)=\"selectedUser = u.user\">\n {{ u.user.firstName }} {{ u.user.lastName }} - {{ u.user.username }}\n <div class=\"email\">{{ u.user.email }}</div>\n <div class=\"user-features\">\n @for (f of u.features; track f.id) {\n <span class=\"chip\">{{ f.label || f.id }}</span>\n }\n </div>\n <!-- <div class=\"user-roles\">\n @for (r of u.user.roles; track $index) {\n <span class=\"chip\">{{ r }}</span>\n }\n </div> -->\n </div>\n }\n </main>\n </section>\n </ng-template>\n</yuv-split-view>\n", styles: [":host main{padding:var(--ymt-spacing-m);flex:1;overflow:hidden;--split-gutter-background-color: transparent}:host section{background-color:var(--ymt-surface);border-radius:var(--ymt-corner);overflow:hidden;display:flex;height:100%;flex-direction:column}:host section header{flex:0 0 auto;display:flex;align-items:center;gap:1em;padding:var(--ymt-spacing-m);border-block-end:1px solid var(--ymt-outline-variant);position:relative;justify-content:space-between}:host section header>div{display:flex;align-items:center;gap:var(--ymt-spacing-m)}:host section header .progress-bar{position:absolute;inset-block-end:0;inset-inline-start:0}:host section main,:host section .main{flex:1;overflow-y:auto;background-color:var(--ymt-surface-container)}:host section main>div,:host section .main>div{padding:var(--ymt-spacing-m)}:host .users{flex:1}:host .users main>div{padding:var(--ymt-spacing-xs);border-radius:var(--ymt-corner-s);margin-block-end:var(--ymt-spacing-m);border:1px solid var(--ymt-outline-variant);background-color:var(--ymt-surface);display:flex;flex-direction:column;gap:.25em}:host .users main>div .email{font-style:italic}:host .users main>div .user-features span.chip{background:var(--mat-sys-primary-container);color:var(--mat-sys-on-primary-container)}:host .users main>div span.chip{font:var(--ymt-font-body-subtle);display:inline-block;border:1px solid var(--ymt-outline-variant);padding:2px 4px;max-width:100px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-inline-end:4px;border-radius:2px}:host .features{flex:1}:host .features .feature{max-width:600px;margin:auto;border:1px solid var(--ymt-outline-variant);background-color:var(--ymt-surface);padding:var(--ymt-spacing-m);margin-block-end:var(--ymt-spacing-m);display:grid;column-gap:var(--ymt-spacing-m);border-radius:var(--ymt-corner-s);grid-template-columns:1fr auto;grid-template-rows:auto auto auto auto;grid-template-areas:\"ext icon\" \"id id\" \"label label\" \"allowed allowed\" \"denied denied\"}:host .features .feature:hover{border-color:currentColor}:host .features .feature:hover button{opacity:1}:host .features .feature .ext{grid-area:ext;padding-block-end:1em}:host .features .feature .id{grid-area:id}:host .features .feature .label{grid-area:label}:host .features .feature .allowed{grid-area:allowed}:host .features .feature .denied{grid-area:denied}:host .features .feature button{grid-area:icon;opacity:.2}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "ngmodule", type: YuvSplitViewModule }, { kind: "directive", type: i2.SplitAreaDirective, selector: "[yuvSplitArea]", inputs: ["size", "minSize", "maxSize", "panelClass", "visible"] }, { kind: "component", type: i2.SplitViewComponent, selector: "yuv-split-view", inputs: ["direction", "gutterSize", "restrictMove", "disabled", "layoutSettingsID"], outputs: ["layoutSettingsChange", "dragStart", "dragEnd", "gutterClick", "gutterDblClick"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i5.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i6.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i8.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: YuvFormsModule }, { kind: "ngmodule", type: YuvAutocompleteModule }, { kind: "component", type: i9.AutocompleteComponent, selector: "yuv-autocomplete", inputs: ["ariaLabel", "busy", "multiple", "distinctValues", "addOnBlur", "minLength", "maxItems", "forceSelection", "autocompleteValues"], outputs: ["autocompleteFnc"] }, { kind: "component", type: MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
227
+ }
228
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: AppsPageComponent, decorators: [{
229
+ type: Component,
230
+ args: [{ selector: 'ysfa-apps', imports: [
231
+ CommonModule,
232
+ ReactiveFormsModule,
233
+ MatCardModule,
234
+ YuvSplitViewModule,
235
+ MatFormFieldModule,
236
+ TranslateModule,
237
+ MatButtonModule,
238
+ MatCheckboxModule,
239
+ MatIconModule,
240
+ MatInputModule,
241
+ YuvFormsModule,
242
+ YuvAutocompleteModule,
243
+ MatProgressBar,
244
+ MatTooltipModule
245
+ ], template: "<yuv-split-view [gutterSize]=\"32\">\n <!-- features -->\n <ng-template yuvSplitArea [size]=\"60\">\n <section class=\"features\">\n <header>\n <h2>\n {{ 'yuv.app.shell-admin.features.headline' | translate }}\n </h2>\n\n @if (hasFeatureConfig) {\n <div class=\"export\">\n <button [disabled]=\"busy()\" mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.config.export.tooltip' | translate\" (click)=\"export()\">\n <mat-icon>download</mat-icon>\n </button>\n <button [disabled]=\"busy()\" mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.config.import.tooltip' | translate\" (click)=\"import()\">\n <mat-icon>upload</mat-icon>\n </button>\n </div>\n\n <div class=\"buttons\">\n <button mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.features.config.add.tooltip' | translate\" [disabled]=\"busy()\" (click)=\"addFeature()\"><mat-icon>add</mat-icon></button>\n <button [disabled]=\"featuresForm.invalid || featuresForm.pristine || busy()\" mat-flat-button (click)=\"save()\">\n {{ 'yuv.app.shell-admin.features.config.save' | translate }}\n </button>\n <button mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.config.delete.tooltip' | translate\" [disabled]=\"busy()\" (click)=\"delete()\"><mat-icon>delete</mat-icon></button>\n </div>\n } @else {\n <button mat-flat-button [disabled]=\"busy()\" (click)=\"createFeatureConfigObject()\">\n {{ 'yuv.app.shell-admin.features.config.create' | translate }}\n </button>\n }\n\n @if (busy()) {\n <mat-progress-bar mode=\"indeterminate\" class=\"progress-bar\"></mat-progress-bar>\n }\n </header>\n\n <form class=\"main\" [formGroup]=\"featuresForm\">\n <div formArrayName=\"features\">\n @for (fForm of featureArray.controls; track $index) {\n <section class=\"feature\" [formGroup]=\"getFeatureForm(fForm)\">\n <button mat-icon-button (click)=\"removeFeature($index)\"><mat-icon>delete</mat-icon></button>\n\n <div class=\"ext\">\n <mat-checkbox formControlName=\"extension\">{{ 'yuv.app.shell-admin.features.config.feature.extension' | translate }}</mat-checkbox>\n </div>\n\n <mat-form-field class=\"id\">\n <mat-label>{{ 'yuv.app.shell-admin.features.config.feature.id' | translate }}</mat-label>\n <input matInput formControlName=\"id\" />\n </mat-form-field>\n\n <mat-form-field class=\"label\">\n <mat-label>{{ 'yuv.app.shell-admin.features.config.feature.label' | translate }}</mat-label>\n <input matInput formControlName=\"label\" />\n </mat-form-field>\n\n <mat-form-field class=\"allowed\">\n <mat-label>{{ 'yuv.app.shell-admin.features.config.feature.allowed' | translate }}</mat-label>\n <yuv-autocomplete\n formControlName=\"allowed\"\n [multiple]=\"true\"\n [forceSelection]=\"true\"\n [distinctValues]=\"true\"\n (autocompleteFnc)=\"fetchRoles($event)\"\n [autocompleteValues]=\"autocompleteValues\"\n ></yuv-autocomplete>\n </mat-form-field>\n\n <mat-form-field class=\"denied\">\n <mat-label>{{ 'yuv.app.shell-admin.features.config.feature.denied' | translate }}</mat-label>\n <yuv-autocomplete\n formControlName=\"denied\"\n [multiple]=\"true\"\n [forceSelection]=\"true\"\n [distinctValues]=\"true\"\n (autocompleteFnc)=\"fetchRoles($event)\"\n [autocompleteValues]=\"autocompleteValues\"\n ></yuv-autocomplete>\n </mat-form-field>\n </section>\n }\n </div>\n </form>\n </section>\n </ng-template>\n\n <!-- users -->\n <ng-template yuvSplitArea [size]=\"40\">\n <section class=\"users\">\n <header>\n <h2>\n {{ 'yuv.app.shell-admin.features.users.headline' | translate }}\n </h2>\n @if (loadingUsers()) {\n <mat-progress-bar mode=\"indeterminate\" class=\"progress-bar\"></mat-progress-bar>\n }\n </header>\n <main>\n @for (u of mappedUsers; track u.user.id) {\n <div (click)=\"selectedUser = u.user\">\n {{ u.user.firstName }} {{ u.user.lastName }} - {{ u.user.username }}\n <div class=\"email\">{{ u.user.email }}</div>\n <div class=\"user-features\">\n @for (f of u.features; track f.id) {\n <span class=\"chip\">{{ f.label || f.id }}</span>\n }\n </div>\n <!-- <div class=\"user-roles\">\n @for (r of u.user.roles; track $index) {\n <span class=\"chip\">{{ r }}</span>\n }\n </div> -->\n </div>\n }\n </main>\n </section>\n </ng-template>\n</yuv-split-view>\n", styles: [":host main{padding:var(--ymt-spacing-m);flex:1;overflow:hidden;--split-gutter-background-color: transparent}:host section{background-color:var(--ymt-surface);border-radius:var(--ymt-corner);overflow:hidden;display:flex;height:100%;flex-direction:column}:host section header{flex:0 0 auto;display:flex;align-items:center;gap:1em;padding:var(--ymt-spacing-m);border-block-end:1px solid var(--ymt-outline-variant);position:relative;justify-content:space-between}:host section header>div{display:flex;align-items:center;gap:var(--ymt-spacing-m)}:host section header .progress-bar{position:absolute;inset-block-end:0;inset-inline-start:0}:host section main,:host section .main{flex:1;overflow-y:auto;background-color:var(--ymt-surface-container)}:host section main>div,:host section .main>div{padding:var(--ymt-spacing-m)}:host .users{flex:1}:host .users main>div{padding:var(--ymt-spacing-xs);border-radius:var(--ymt-corner-s);margin-block-end:var(--ymt-spacing-m);border:1px solid var(--ymt-outline-variant);background-color:var(--ymt-surface);display:flex;flex-direction:column;gap:.25em}:host .users main>div .email{font-style:italic}:host .users main>div .user-features span.chip{background:var(--mat-sys-primary-container);color:var(--mat-sys-on-primary-container)}:host .users main>div span.chip{font:var(--ymt-font-body-subtle);display:inline-block;border:1px solid var(--ymt-outline-variant);padding:2px 4px;max-width:100px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-inline-end:4px;border-radius:2px}:host .features{flex:1}:host .features .feature{max-width:600px;margin:auto;border:1px solid var(--ymt-outline-variant);background-color:var(--ymt-surface);padding:var(--ymt-spacing-m);margin-block-end:var(--ymt-spacing-m);display:grid;column-gap:var(--ymt-spacing-m);border-radius:var(--ymt-corner-s);grid-template-columns:1fr auto;grid-template-rows:auto auto auto auto;grid-template-areas:\"ext icon\" \"id id\" \"label label\" \"allowed allowed\" \"denied denied\"}:host .features .feature:hover{border-color:currentColor}:host .features .feature:hover button{opacity:1}:host .features .feature .ext{grid-area:ext;padding-block-end:1em}:host .features .feature .id{grid-area:id}:host .features .feature .label{grid-area:label}:host .features .feature .allowed{grid-area:allowed}:host .features .feature .denied{grid-area:denied}:host .features .feature button{grid-area:icon;opacity:.2}\n"] }]
246
+ }], ctorParameters: () => [] });
247
+
248
+ class TypesPageComponent {
249
+ #fb = inject(FormBuilder);
250
+ #shellConfig = inject(ShellConfigService);
251
+ #shell = inject(ShellService);
252
+ #typeConfigObjectId;
253
+ hasTypeConfig = false;
254
+ types = [];
255
+ busy = signal(false);
256
+ typesForm = this.#fb.group({
257
+ types: this.#fb.array([])
258
+ });
259
+ get typeArray() {
260
+ return this.typesForm.controls['types'];
261
+ }
262
+ getTypeForm(c) {
263
+ return c;
264
+ }
265
+ createTypeConfigObject() {
266
+ this.busy.set(true);
267
+ return this.#shellConfig
268
+ .create(SHELL_CONFIG_TYPES, [])
269
+ .pipe(tap((res) => {
270
+ this.hasTypeConfig = !!res;
271
+ this.#typeConfigObjectId = res.id;
272
+ }), finalize(() => {
273
+ this.busy.set(false);
274
+ }))
275
+ .subscribe();
276
+ }
277
+ addType() {
278
+ this.typeArray.push(this.#fb.group({
279
+ id: ['', Validators.required],
280
+ baseType: [SystemType.DOCUMENT],
281
+ instantApply: [false],
282
+ applicableToMimeType: ['*/*']
283
+ }));
284
+ }
285
+ removeType(idx) {
286
+ this.typeArray.removeAt(idx);
287
+ this.typesForm.markAsDirty();
288
+ }
289
+ #formToTypes() {
290
+ return this.typeArray.value.map((f) => {
291
+ return {
292
+ id: f.id,
293
+ baseType: f.baseType,
294
+ instantApply: f.instantApply,
295
+ applicableToMimeTypes: [f.applicableToMimeType]
296
+ };
297
+ });
298
+ }
299
+ delete() {
300
+ if (this.#typeConfigObjectId) {
301
+ this.busy.set(true);
302
+ this.#shellConfig
303
+ .delete(this.#typeConfigObjectId)
304
+ .pipe(tap(() => {
305
+ this.typeArray.clear();
306
+ this.types = [];
307
+ this.hasTypeConfig = false;
308
+ this.typesForm.markAsPristine();
309
+ }), finalize(() => {
310
+ this.busy.set(false);
311
+ }))
312
+ .subscribe();
313
+ }
314
+ }
315
+ save() {
316
+ this.busy.set(true);
317
+ const types = this.#formToTypes();
318
+ if (this.#typeConfigObjectId) {
319
+ this.#shellConfig
320
+ .update(this.#typeConfigObjectId, types)
321
+ .pipe(finalize(() => {
322
+ this.busy.set(false);
323
+ }))
324
+ .subscribe(() => {
325
+ this.types = types;
326
+ this.typesForm.markAsPristine();
327
+ });
328
+ }
329
+ }
330
+ export() {
331
+ if (this.#typeConfigObjectId) {
332
+ this.#shellConfig.download(this.#typeConfigObjectId, `${SHELL_CONFIG_TYPES}.json`);
333
+ }
334
+ }
335
+ import() {
336
+ let contentStreamId;
337
+ if (this.#typeConfigObjectId)
338
+ this.#shell
339
+ .pickFile()
340
+ .pipe(switchMap((file) => (file ? this.#shellConfig.upload(this.#typeConfigObjectId, file) : of(null))), tap((x) => {
341
+ if (x.content?.contentStreamId !== contentStreamId)
342
+ this.#loadTypes();
343
+ }))
344
+ .subscribe();
345
+ }
346
+ #loadTypes() {
347
+ this.#shellConfig.get(SHELL_CONFIG_TYPES).subscribe((typeConfig) => {
348
+ this.hasTypeConfig = !!typeConfig;
349
+ this.#typeConfigObjectId = typeConfig?.id;
350
+ this.types = typeConfig ? typeConfig.data : [];
351
+ this.types.forEach((type) => {
352
+ const applicableToMimeTypes = type.applicableToMimeTypes || [];
353
+ this.typeArray.push(this.#fb.group({
354
+ id: [type.id, Validators.required],
355
+ baseType: [type.baseType],
356
+ instantApply: [type.instantApply],
357
+ applicableToMimeType: [applicableToMimeTypes[0] || '']
358
+ }));
359
+ });
360
+ });
361
+ }
362
+ ngOnInit() {
363
+ this.#loadTypes();
364
+ }
365
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: TypesPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
366
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.1", type: TypesPageComponent, isStandalone: true, selector: "ymsa-types", ngImport: i0, template: "<header>\n <h2>\n {{ 'yuv.app.shell-admin.types.headline' | translate }}\n </h2>\n\n @if (hasTypeConfig) {\n <div class=\"export\">\n <button [disabled]=\"busy()\" mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.config.export.tooltip' | translate\" (click)=\"export()\">\n <mat-icon>download</mat-icon>\n </button>\n <button [disabled]=\"busy()\" mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.config.import.tooltip' | translate\" (click)=\"import()\">\n <mat-icon>upload</mat-icon>\n </button>\n </div>\n\n <div class=\"buttons\">\n <button [disabled]=\"busy()\" mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.types.config.add.tooltip' | translate\" (click)=\"addType()\"><mat-icon>add</mat-icon></button>\n <button [disabled]=\"typesForm.invalid || typesForm.pristine || busy()\" mat-flat-button (click)=\"save()\">\n {{ 'yuv.app.shell-admin.types.config.save' | translate }}\n </button>\n <button mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.config.delete.tooltip' | translate\" [disabled]=\"busy()\" (click)=\"delete()\"><mat-icon>delete</mat-icon></button>\n </div>\n } @else {\n <button mat-flat-button [disabled]=\"busy()\" (click)=\"createTypeConfigObject()\">{{ 'yuv.app.shell-admin.types.config.create' | translate }}</button>\n }\n @if (busy()) {\n <mat-progress-bar mode=\"indeterminate\" class=\"progress-bar\"></mat-progress-bar>\n }\n</header>\n\n<form [formGroup]=\"typesForm\">\n <div formArrayName=\"types\">\n @for (typeForm of typeArray.controls; track $index) {\n <section class=\"type\" [formGroup]=\"getTypeForm(typeForm)\">\n <button mat-icon-button\n [matTooltip]=\"'yuv.app.shell-admin.types.config.item.delete.tooltip' | translate\"\n (click)=\"removeType($index)\"><mat-icon>delete</mat-icon></button>\n\n <mat-form-field appearance=\"outline\" class=\"id\">\n <mat-label>{{ 'yuv.app.shell-admin.type.config.id' | translate }}</mat-label>\n <input matInput formControlName=\"id\" />\n </mat-form-field>\n\n @let bt = getTypeForm(typeForm).get('baseType')?.value;\n @if (bt === 'system:document' || bt === 'system:object') {\n <mat-form-field class=\"mimetype\">\n <mat-label>{{ 'yuv.app.shell-admin.type.config.mimetype' | translate }}</mat-label>\n <input matInput formControlName=\"applicableToMimeType\" />\n </mat-form-field>\n }\n\n <div class=\"baseType\">\n <mat-radio-group formControlName=\"baseType\">\n <mat-radio-button value=\"system:document\">Document</mat-radio-button>\n <mat-radio-button value=\"system:folder\">Folder</mat-radio-button>\n <mat-radio-button value=\"system:object\">Both</mat-radio-button>\n </mat-radio-group>\n </div>\n\n <div class=\"flags\">\n <mat-checkbox formControlName=\"instantApply\">{{ 'yuv.app.shell-admin.type.config.instant-apply' | translate }}</mat-checkbox>\n <div class=\"explain\">{{ 'yuv.app.shell-admin.type.config.instant-apply.explain' | translate }}</div>\n </div>\n </section>\n }\n </div>\n</form>\n", styles: [":host{overflow:hidden;display:flex;height:100%;flex-direction:column;background-color:var(--ymt-surface);border-radius:var(--ymt-corner)}:host header{flex:0 0 auto;display:flex;align-items:center;gap:1em;padding:var(--ymt-spacing-m);border-block-end:1px solid var(--ymt-outline-variant);position:relative;justify-content:space-between}:host header>div{display:flex;align-items:center;gap:var(--ymt-spacing-m)}:host header .progress-bar{position:absolute;inset-block-end:0;inset-inline-start:0}:host form{flex:1;overflow-y:auto;background-color:var(--ymt-surface-container)}:host form>div{padding:var(--ymt-spacing-m)}:host .type{max-width:600px;margin:auto;border:1px solid var(--ymt-outline-variant);background-color:var(--ymt-surface);padding:var(--ymt-spacing-m);margin-block-end:var(--ymt-spacing-m);display:grid;column-gap:var(--ymt-spacing-m);border-radius:var(--ymt-corner-s);grid-template-columns:1fr auto;grid-template-rows:auto auto;grid-template-areas:\"id icon\" \"base base\" \"mimetype mimetype\" \"flags flags\"}:host .type:hover{border-color:currentColor}:host .type:hover button{opacity:1}:host .type .id{grid-area:id}:host .type .mimetype{grid-area:mimetype}:host .type .baseType{grid-area:base}:host .type .flags{grid-area:flags}:host .type button{grid-area:icon;opacity:.2}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "ngmodule", type: MatRadioModule }, { kind: "directive", type: i2$1.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i2$1.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i8.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i6.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i5.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i7.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: TranslateModule$1 }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }, { kind: "component", type: MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i10.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
367
+ }
368
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: TypesPageComponent, decorators: [{
369
+ type: Component,
370
+ args: [{ selector: 'ymsa-types', imports: [
371
+ CommonModule,
372
+ ReactiveFormsModule,
373
+ MatRadioModule,
374
+ MatFormFieldModule,
375
+ MatInputModule,
376
+ MatCheckboxModule,
377
+ MatButtonModule,
378
+ MatIconModule,
379
+ TranslateModule$1,
380
+ MatProgressBar,
381
+ MatTooltipModule
382
+ ], template: "<header>\n <h2>\n {{ 'yuv.app.shell-admin.types.headline' | translate }}\n </h2>\n\n @if (hasTypeConfig) {\n <div class=\"export\">\n <button [disabled]=\"busy()\" mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.config.export.tooltip' | translate\" (click)=\"export()\">\n <mat-icon>download</mat-icon>\n </button>\n <button [disabled]=\"busy()\" mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.config.import.tooltip' | translate\" (click)=\"import()\">\n <mat-icon>upload</mat-icon>\n </button>\n </div>\n\n <div class=\"buttons\">\n <button [disabled]=\"busy()\" mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.types.config.add.tooltip' | translate\" (click)=\"addType()\"><mat-icon>add</mat-icon></button>\n <button [disabled]=\"typesForm.invalid || typesForm.pristine || busy()\" mat-flat-button (click)=\"save()\">\n {{ 'yuv.app.shell-admin.types.config.save' | translate }}\n </button>\n <button mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.config.delete.tooltip' | translate\" [disabled]=\"busy()\" (click)=\"delete()\"><mat-icon>delete</mat-icon></button>\n </div>\n } @else {\n <button mat-flat-button [disabled]=\"busy()\" (click)=\"createTypeConfigObject()\">{{ 'yuv.app.shell-admin.types.config.create' | translate }}</button>\n }\n @if (busy()) {\n <mat-progress-bar mode=\"indeterminate\" class=\"progress-bar\"></mat-progress-bar>\n }\n</header>\n\n<form [formGroup]=\"typesForm\">\n <div formArrayName=\"types\">\n @for (typeForm of typeArray.controls; track $index) {\n <section class=\"type\" [formGroup]=\"getTypeForm(typeForm)\">\n <button mat-icon-button\n [matTooltip]=\"'yuv.app.shell-admin.types.config.item.delete.tooltip' | translate\"\n (click)=\"removeType($index)\"><mat-icon>delete</mat-icon></button>\n\n <mat-form-field appearance=\"outline\" class=\"id\">\n <mat-label>{{ 'yuv.app.shell-admin.type.config.id' | translate }}</mat-label>\n <input matInput formControlName=\"id\" />\n </mat-form-field>\n\n @let bt = getTypeForm(typeForm).get('baseType')?.value;\n @if (bt === 'system:document' || bt === 'system:object') {\n <mat-form-field class=\"mimetype\">\n <mat-label>{{ 'yuv.app.shell-admin.type.config.mimetype' | translate }}</mat-label>\n <input matInput formControlName=\"applicableToMimeType\" />\n </mat-form-field>\n }\n\n <div class=\"baseType\">\n <mat-radio-group formControlName=\"baseType\">\n <mat-radio-button value=\"system:document\">Document</mat-radio-button>\n <mat-radio-button value=\"system:folder\">Folder</mat-radio-button>\n <mat-radio-button value=\"system:object\">Both</mat-radio-button>\n </mat-radio-group>\n </div>\n\n <div class=\"flags\">\n <mat-checkbox formControlName=\"instantApply\">{{ 'yuv.app.shell-admin.type.config.instant-apply' | translate }}</mat-checkbox>\n <div class=\"explain\">{{ 'yuv.app.shell-admin.type.config.instant-apply.explain' | translate }}</div>\n </div>\n </section>\n }\n </div>\n</form>\n", styles: [":host{overflow:hidden;display:flex;height:100%;flex-direction:column;background-color:var(--ymt-surface);border-radius:var(--ymt-corner)}:host header{flex:0 0 auto;display:flex;align-items:center;gap:1em;padding:var(--ymt-spacing-m);border-block-end:1px solid var(--ymt-outline-variant);position:relative;justify-content:space-between}:host header>div{display:flex;align-items:center;gap:var(--ymt-spacing-m)}:host header .progress-bar{position:absolute;inset-block-end:0;inset-inline-start:0}:host form{flex:1;overflow-y:auto;background-color:var(--ymt-surface-container)}:host form>div{padding:var(--ymt-spacing-m)}:host .type{max-width:600px;margin:auto;border:1px solid var(--ymt-outline-variant);background-color:var(--ymt-surface);padding:var(--ymt-spacing-m);margin-block-end:var(--ymt-spacing-m);display:grid;column-gap:var(--ymt-spacing-m);border-radius:var(--ymt-corner-s);grid-template-columns:1fr auto;grid-template-rows:auto auto;grid-template-areas:\"id icon\" \"base base\" \"mimetype mimetype\" \"flags flags\"}:host .type:hover{border-color:currentColor}:host .type:hover button{opacity:1}:host .type .id{grid-area:id}:host .type .mimetype{grid-area:mimetype}:host .type .baseType{grid-area:base}:host .type .flags{grid-area:flags}:host .type button{grid-area:icon;opacity:.2}\n"] }]
383
+ }] });
384
+
385
+ class ShellAdminComponent {
386
+ // user token has to be present because this is a part of the
387
+ // required bootstrap process
388
+ user = inject(YUV_USER);
389
+ // only admins should be able to see this page
390
+ isValidUser = this.user?.authorities.includes('YUUVIS_TENANT_ADMIN');
391
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: ShellAdminComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
392
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.1", type: ShellAdminComponent, isStandalone: true, selector: "ymsa-shell-admin", ngImport: i0, template: "<header>\n <a routerLink=\"./\">yM Shell Admin </a>\n <nav>\n <a routerLink=\"apps\" routerLinkActive=\"active\">{{ 'yuv.app.shell-admin.nav.apps' | translate }}</a>\n <a routerLink=\"types\" routerLinkActive=\"active\">{{ 'yuv.app.shell-admin.nav.types' | translate }}</a>\n </nav>\n \n</header>\n@if (isValidUser) {\n <main>\n <router-outlet></router-outlet>\n </main>\n} @else {\n <main class=\"invalid-user\">\n <p>{{ 'yuv.app.shell-admin.invalid-user' | translate }}</p>\n </main>\n}\n", styles: [":host{display:flex;flex-direction:column;height:100%;box-sizing:border-box;gap:var(--ymt-spacing-3xl);padding:var(--ymt-spacing-3xl)}:host header{display:flex;justify-content:space-between;align-items:center;padding:var(--ymt-spacing-m) var(--ymt-spacing-3xl);background-color:var(--ymt-surface);color:var(--ymt-text-color);border-radius:var(--ymt-corner)}:host header a{text-decoration:none;font-weight:700}:host header nav{display:flex;gap:var(--ymt-spacing-m)}:host header nav a{font-weight:400;padding:var(--ymt-spacing-2xs) var(--ymt-spacing-m);border-radius:var(--ymt-corner-s)}:host header nav a.active{color:var(--ymt-text-color);background-color:var(--ymt-surface-container-high)}:host main{flex:1;overflow:hidden}:host main.invalid-user{display:grid;place-items:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i1$1.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "ngmodule", type: TranslateModule$1 }, { kind: "pipe", type: i4.TranslatePipe, name: "translate" }] });
393
+ }
394
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: ShellAdminComponent, decorators: [{
395
+ type: Component,
396
+ args: [{ selector: 'ymsa-shell-admin', imports: [CommonModule, RouterModule, TranslateModule$1], template: "<header>\n <a routerLink=\"./\">yM Shell Admin </a>\n <nav>\n <a routerLink=\"apps\" routerLinkActive=\"active\">{{ 'yuv.app.shell-admin.nav.apps' | translate }}</a>\n <a routerLink=\"types\" routerLinkActive=\"active\">{{ 'yuv.app.shell-admin.nav.types' | translate }}</a>\n </nav>\n \n</header>\n@if (isValidUser) {\n <main>\n <router-outlet></router-outlet>\n </main>\n} @else {\n <main class=\"invalid-user\">\n <p>{{ 'yuv.app.shell-admin.invalid-user' | translate }}</p>\n </main>\n}\n", styles: [":host{display:flex;flex-direction:column;height:100%;box-sizing:border-box;gap:var(--ymt-spacing-3xl);padding:var(--ymt-spacing-3xl)}:host header{display:flex;justify-content:space-between;align-items:center;padding:var(--ymt-spacing-m) var(--ymt-spacing-3xl);background-color:var(--ymt-surface);color:var(--ymt-text-color);border-radius:var(--ymt-corner)}:host header a{text-decoration:none;font-weight:700}:host header nav{display:flex;gap:var(--ymt-spacing-m)}:host header nav a{font-weight:400;padding:var(--ymt-spacing-2xs) var(--ymt-spacing-m);border-radius:var(--ymt-corner-s)}:host header nav a.active{color:var(--ymt-text-color);background-color:var(--ymt-surface-container-high)}:host main{flex:1;overflow:hidden}:host main.invalid-user{display:grid;place-items:center}\n"] }]
397
+ }] });
398
+
399
+ class HomePageComponent {
400
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: HomePageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
401
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.1", type: HomePageComponent, isStandalone: true, selector: "ymsa-home", ngImport: i0, template: "\n\n\n<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" ><path d=\"M680-280q25 0 42.5-17.5T740-340q0-25-17.5-42.5T680-400q-25 0-42.5 17.5T620-340q0 25 17.5 42.5T680-280Zm0 120q31 0 57-14.5t42-38.5q-22-13-47-20t-52-7q-27 0-52 7t-47 20q16 24 42 38.5t57 14.5ZM480-80q-139-35-229.5-159.5T160-516v-244l320-120 320 120v227q-19-8-39-14.5t-41-9.5v-147l-240-90-240 90v188q0 47 12.5 94t35 89.5Q310-290 342-254t71 60q11 32 29 61t41 52q-1 0-1.5.5t-1.5.5Zm200 0q-83 0-141.5-58.5T480-280q0-83 58.5-141.5T680-480q83 0 141.5 58.5T880-280q0 83-58.5 141.5T680-80ZM480-494Z\"/></svg>\n", styles: [":host{display:grid;grid-template-columns:1fr;grid-template-rows:1fr;height:100%;align-items:center;justify-items:center}:host svg{fill:var(--ymt-text-color);height:50vh;width:50vh;grid-row:1;grid-column:1;opacity:.1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
402
+ }
403
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: HomePageComponent, decorators: [{
404
+ type: Component,
405
+ args: [{ selector: 'ymsa-home', imports: [CommonModule], template: "\n\n\n<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" ><path d=\"M680-280q25 0 42.5-17.5T740-340q0-25-17.5-42.5T680-400q-25 0-42.5 17.5T620-340q0 25 17.5 42.5T680-280Zm0 120q31 0 57-14.5t42-38.5q-22-13-47-20t-52-7q-27 0-52 7t-47 20q16 24 42 38.5t57 14.5ZM480-80q-139-35-229.5-159.5T160-516v-244l320-120 320 120v227q-19-8-39-14.5t-41-9.5v-147l-240-90-240 90v188q0 47 12.5 94t35 89.5Q310-290 342-254t71 60q11 32 29 61t41 52q-1 0-1.5.5t-1.5.5Zm200 0q-83 0-141.5-58.5T480-280q0-83 58.5-141.5T680-480q83 0 141.5 58.5T880-280q0 83-58.5 141.5T680-80ZM480-494Z\"/></svg>\n", styles: [":host{display:grid;grid-template-columns:1fr;grid-template-rows:1fr;height:100%;align-items:center;justify-items:center}:host svg{fill:var(--ymt-text-color);height:50vh;width:50vh;grid-row:1;grid-column:1;opacity:.1}\n"] }]
406
+ }] });
407
+
408
+ const FeatureAdminRoutes = [
409
+ { path: '', component: ShellAdminComponent, children: [
410
+ { path: '', component: HomePageComponent },
411
+ { path: 'apps', component: AppsPageComponent },
412
+ { path: 'types', component: TypesPageComponent },
413
+ ] },
414
+ ];
415
+
416
+ /**
417
+ * Generated bundle index. Do not edit.
418
+ */
419
+
420
+ export { FeatureAdminRoutes };
421
+ //# sourceMappingURL=yuuvis-app-shell-admin.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yuuvis-app-shell-admin.mjs","sources":["../../../../../libs/apps/shell-admin/src/lib/services/apps/apps.service.ts","../../../../../libs/apps/shell-admin/src/lib/pages/apps/apps.component.ts","../../../../../libs/apps/shell-admin/src/lib/pages/apps/apps.component.html","../../../../../libs/apps/shell-admin/src/lib/pages/types/types.component.ts","../../../../../libs/apps/shell-admin/src/lib/pages/types/types.component.html","../../../../../libs/apps/shell-admin/src/lib/shell-admin.component.ts","../../../../../libs/apps/shell-admin/src/lib/shell-admin.component.html","../../../../../libs/apps/shell-admin/src/lib/pages/home/home.component.ts","../../../../../libs/apps/shell-admin/src/lib/pages/home/home.component.html","../../../../../libs/apps/shell-admin/src/lib/lib.routes.ts","../../../../../libs/apps/shell-admin/src/yuuvis-app-shell-admin.ts"],"sourcesContent":["import { inject, Injectable } from '@angular/core';\nimport { BackendService } from '@yuuvis/client-core';\nimport { Observable, map } from 'rxjs';\nimport { TmRole, TmUser } from './apps.interface';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class AppsService {\n #backend = inject(BackendService);\n\n getUsers(): Observable<TmUser[]> {\n return this.#backend.get(`/tenant-management/api/admin/users`, '').pipe(\n map((response: any) => {\n return response.map((user: any) => {\n const { id, username, createdTimestamp, email, enabled, firstName, lastName, groups, roles } = user;\n return { id, username, createdTimestamp, email, enabled, firstName, lastName, groups, roles };\n });\n })\n );\n }\n\n getRoles(): Observable<TmRole[]> {\n return this.#backend.get(`/tenant-management/api/admin/roles`, '');\n }\n}\n","import { CommonModule } from '@angular/common';\nimport { Component, inject, signal } from '@angular/core';\nimport { AbstractControl, FormArray, FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatCardModule } from '@angular/material/card';\nimport { MatCheckboxModule } from '@angular/material/checkbox';\nimport { MatFormFieldModule } from '@angular/material/form-field';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatInputModule } from '@angular/material/input';\nimport { TranslateModule } from '@ngx-translate/core';\nimport { YUV_USER, YuvUser } from '@yuuvis/client-core';\nimport { AutocompleteItem, YuvAutocompleteModule } from '@yuuvis/client-framework/autocomplete';\nimport { YuvFormsModule } from '@yuuvis/client-framework/forms';\nimport { YuvSplitViewModule } from '@yuuvis/client-framework/split-view';\nimport { ClientShellFeature, FeatureService, ShellService } from '@yuuvis/client-shell-core';\nimport { finalize } from 'rxjs';\nimport { TmRole, TmUser } from '../../services/apps/apps.interface';\nimport { AppsService } from '../../services/apps/apps.service';\nimport { MatProgressBar } from '@angular/material/progress-bar';\nimport { MatTooltipModule } from '@angular/material/tooltip';\n\n@Component({\n selector: 'ysfa-apps',\n imports: [\n CommonModule,\n ReactiveFormsModule,\n MatCardModule,\n YuvSplitViewModule,\n MatFormFieldModule,\n TranslateModule,\n MatButtonModule,\n MatCheckboxModule,\n MatIconModule,\n MatInputModule,\n YuvFormsModule,\n YuvAutocompleteModule,\n MatProgressBar,\n MatTooltipModule\n ],\n templateUrl: './apps.component.html',\n styleUrl: './apps.component.scss'\n})\nexport class AppsPageComponent {\n #appsService = inject(AppsService);\n #features = inject(FeatureService);\n #fb = inject(FormBuilder);\n #shell = inject(ShellService);\n\n // user token has to be present because this is a part of the\n // required bootstrap process\n user = inject<YuvUser>(YUV_USER);\n // only admins should be able to see this page\n isValidUser = this.user?.authorities.includes('YUUVIS_TENANT_ADMIN');\n\n users: TmUser[] = [];\n mappedUsers: {\n features: ClientShellFeature[];\n user: TmUser;\n }[] = [];\n roles: TmRole[] = [];\n features: ClientShellFeature[] = [];\n selectedUser?: TmUser;\n autocompleteValues: AutocompleteItem[] = [];\n\n busy = signal<boolean>(false);\n loadingUsers = signal<boolean>(false);\n\n featuresForm = this.#fb.group({\n features: this.#fb.array([])\n });\n\n get featureArray(): FormArray {\n return this.featuresForm.controls['features'] as FormArray;\n }\n\n getFeatureForm(c: AbstractControl<any, any>): FormGroup {\n return c as FormGroup;\n }\n\n hasFeatureConfig = this.#features.hasFeatureConfig();\n\n constructor() {\n this.#fetchRoles();\n this.#fetchUsers();\n // this.features = this.#features.getFeatures();\n\n this.featuresForm.valueChanges.subscribe((v) => {\n this.#mapUsersToFeatures();\n });\n\n // this.features.forEach((feature) => {\n // this.featureArray.push(\n // this.#fb.group({\n // id: [feature.id, Validators.required],\n // label: [feature.label],\n // extension: [feature.extension],\n // allowed: [(feature.allowed || []).map((a) => ({ label: a, value: a }))],\n // denied: [(feature.denied || []).map((d) => ({ label: d, value: d }))]\n // })\n // );\n // });\n this.#loadFeatures();\n }\n\n #loadFeatures() {\n this.features = this.#features.getFeatures();\n this.featuresForm.patchValue({\n features: []\n })\n this.features.forEach((feature) => {\n this.featureArray.push(\n this.#fb.group({\n id: [feature.id, Validators.required],\n label: [feature.label],\n extension: [feature.extension],\n allowed: [(feature.allowed || []).map((a) => ({ label: a, value: a }))],\n denied: [(feature.denied || []).map((d) => ({ label: d, value: d }))]\n })\n );\n });\n }\n\n createFeatureConfigObject() {\n this.busy.set(true);\n this.#features\n .createSettingsObject()\n .pipe(\n finalize(() => {\n this.busy.set(false);\n })\n )\n .subscribe(() => {\n this.hasFeatureConfig = this.#features.hasFeatureConfig();\n });\n }\n\n #fetchUsers() {\n this.loadingUsers.set(true);\n this.#appsService\n .getUsers()\n .pipe(\n finalize(() => {\n this.loadingUsers.set(false);\n })\n )\n .subscribe((users) => {\n this.users = users;\n this.#mapUsersToFeatures();\n });\n }\n\n #mapUsersToFeatures() {\n // map form to features\n const features = this.#formToFeatures();\n\n this.mappedUsers = this.users.map((user) => {\n return {\n user,\n features: features.filter((f) => this.#features.canUseFeature(f, user.roles))\n };\n });\n }\n\n #formToFeatures(): ClientShellFeature[] {\n return this.featureArray.value.map((f: any) => {\n return {\n id: f.id,\n label: f.label,\n extension: f.extension,\n allowed: f.allowed.map((a: AutocompleteItem) => a.value),\n denied: f.denied.map((d: AutocompleteItem) => d.value)\n };\n });\n }\n\n #fetchRoles() {\n this.#appsService.getRoles().subscribe((roles) => {\n this.roles = roles;\n this.autocompleteValues = roles.map((role) => ({\n label: role.name,\n value: role.name\n }));\n });\n }\n\n addFeature() {\n this.featureArray.push(\n this.#fb.group({\n id: ['', Validators.required],\n label: [''],\n extension: [false],\n allowed: [[]],\n denied: [[]]\n })\n );\n }\n\n removeFeature(idx: number) {\n this.featureArray.removeAt(idx);\n this.featuresForm.markAsDirty();\n }\n\n fetchRoles(t: string) {\n this.autocompleteValues = this.roles\n .filter((role) => role.name.toLowerCase().includes(t.toLowerCase()))\n .map((role) => ({\n label: role.name,\n value: role.name\n }));\n }\n\n delete() {\n this.busy.set(true);\n this.#features\n .deleteFeatureSettings()\n .pipe(\n finalize(() => {\n this.busy.set(false);\n })\n )\n .subscribe(() => {\n this.featureArray.clear();\n this.features = [];\n this.hasFeatureConfig = false;\n this.featuresForm.markAsPristine();\n });\n }\n\n save() {\n this.busy.set(true);\n const features = this.#formToFeatures();\n this.#features\n .saveFeatureSettings(features)\n .pipe(\n finalize(() => {\n this.busy.set(false);\n })\n )\n .subscribe(() => this.featuresForm.markAsPristine());\n }\n\n export() {\n this.#features.export();\n }\n\n import() {\n this.#features.import().subscribe()\n }\n}\n","<yuv-split-view [gutterSize]=\"32\">\n <!-- features -->\n <ng-template yuvSplitArea [size]=\"60\">\n <section class=\"features\">\n <header>\n <h2>\n {{ 'yuv.app.shell-admin.features.headline' | translate }}\n </h2>\n\n @if (hasFeatureConfig) {\n <div class=\"export\">\n <button [disabled]=\"busy()\" mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.config.export.tooltip' | translate\" (click)=\"export()\">\n <mat-icon>download</mat-icon>\n </button>\n <button [disabled]=\"busy()\" mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.config.import.tooltip' | translate\" (click)=\"import()\">\n <mat-icon>upload</mat-icon>\n </button>\n </div>\n\n <div class=\"buttons\">\n <button mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.features.config.add.tooltip' | translate\" [disabled]=\"busy()\" (click)=\"addFeature()\"><mat-icon>add</mat-icon></button>\n <button [disabled]=\"featuresForm.invalid || featuresForm.pristine || busy()\" mat-flat-button (click)=\"save()\">\n {{ 'yuv.app.shell-admin.features.config.save' | translate }}\n </button>\n <button mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.config.delete.tooltip' | translate\" [disabled]=\"busy()\" (click)=\"delete()\"><mat-icon>delete</mat-icon></button>\n </div>\n } @else {\n <button mat-flat-button [disabled]=\"busy()\" (click)=\"createFeatureConfigObject()\">\n {{ 'yuv.app.shell-admin.features.config.create' | translate }}\n </button>\n }\n\n @if (busy()) {\n <mat-progress-bar mode=\"indeterminate\" class=\"progress-bar\"></mat-progress-bar>\n }\n </header>\n\n <form class=\"main\" [formGroup]=\"featuresForm\">\n <div formArrayName=\"features\">\n @for (fForm of featureArray.controls; track $index) {\n <section class=\"feature\" [formGroup]=\"getFeatureForm(fForm)\">\n <button mat-icon-button (click)=\"removeFeature($index)\"><mat-icon>delete</mat-icon></button>\n\n <div class=\"ext\">\n <mat-checkbox formControlName=\"extension\">{{ 'yuv.app.shell-admin.features.config.feature.extension' | translate }}</mat-checkbox>\n </div>\n\n <mat-form-field class=\"id\">\n <mat-label>{{ 'yuv.app.shell-admin.features.config.feature.id' | translate }}</mat-label>\n <input matInput formControlName=\"id\" />\n </mat-form-field>\n\n <mat-form-field class=\"label\">\n <mat-label>{{ 'yuv.app.shell-admin.features.config.feature.label' | translate }}</mat-label>\n <input matInput formControlName=\"label\" />\n </mat-form-field>\n\n <mat-form-field class=\"allowed\">\n <mat-label>{{ 'yuv.app.shell-admin.features.config.feature.allowed' | translate }}</mat-label>\n <yuv-autocomplete\n formControlName=\"allowed\"\n [multiple]=\"true\"\n [forceSelection]=\"true\"\n [distinctValues]=\"true\"\n (autocompleteFnc)=\"fetchRoles($event)\"\n [autocompleteValues]=\"autocompleteValues\"\n ></yuv-autocomplete>\n </mat-form-field>\n\n <mat-form-field class=\"denied\">\n <mat-label>{{ 'yuv.app.shell-admin.features.config.feature.denied' | translate }}</mat-label>\n <yuv-autocomplete\n formControlName=\"denied\"\n [multiple]=\"true\"\n [forceSelection]=\"true\"\n [distinctValues]=\"true\"\n (autocompleteFnc)=\"fetchRoles($event)\"\n [autocompleteValues]=\"autocompleteValues\"\n ></yuv-autocomplete>\n </mat-form-field>\n </section>\n }\n </div>\n </form>\n </section>\n </ng-template>\n\n <!-- users -->\n <ng-template yuvSplitArea [size]=\"40\">\n <section class=\"users\">\n <header>\n <h2>\n {{ 'yuv.app.shell-admin.features.users.headline' | translate }}\n </h2>\n @if (loadingUsers()) {\n <mat-progress-bar mode=\"indeterminate\" class=\"progress-bar\"></mat-progress-bar>\n }\n </header>\n <main>\n @for (u of mappedUsers; track u.user.id) {\n <div (click)=\"selectedUser = u.user\">\n {{ u.user.firstName }} {{ u.user.lastName }} - {{ u.user.username }}\n <div class=\"email\">{{ u.user.email }}</div>\n <div class=\"user-features\">\n @for (f of u.features; track f.id) {\n <span class=\"chip\">{{ f.label || f.id }}</span>\n }\n </div>\n <!-- <div class=\"user-roles\">\n @for (r of u.user.roles; track $index) {\n <span class=\"chip\">{{ r }}</span>\n }\n </div> -->\n </div>\n }\n </main>\n </section>\n </ng-template>\n</yuv-split-view>\n","import { CommonModule } from '@angular/common';\nimport { Component, inject, OnInit, signal } from '@angular/core';\nimport { AbstractControl, FormArray, FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatCheckboxModule } from '@angular/material/checkbox';\nimport { MatFormFieldModule } from '@angular/material/form-field';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatInputModule } from '@angular/material/input';\nimport { MatRadioModule } from '@angular/material/radio';\nimport { DmsObject, SystemType, TranslateModule } from '@yuuvis/client-core';\nimport { ClientShellType, SHELL_CONFIG_TYPES, ShellConfigService, ShellService } from '@yuuvis/client-shell-core';\nimport { finalize, of, switchMap, tap } from 'rxjs';\nimport { MatProgressBar } from '@angular/material/progress-bar';\nimport { MatTooltipModule } from '@angular/material/tooltip';\n\n@Component({\n selector: 'ymsa-types',\n imports: [\n CommonModule,\n ReactiveFormsModule,\n MatRadioModule,\n MatFormFieldModule,\n MatInputModule,\n MatCheckboxModule,\n MatButtonModule,\n MatIconModule,\n TranslateModule,\n MatProgressBar,\n MatTooltipModule\n ],\n templateUrl: './types.component.html',\n styleUrl: './types.component.scss'\n})\nexport class TypesPageComponent implements OnInit {\n #fb = inject(FormBuilder);\n #shellConfig = inject(ShellConfigService);\n #shell = inject(ShellService);\n\n #typeConfigObjectId?: string;\n hasTypeConfig = false;\n types: ClientShellType[] = [];\n\n busy = signal<boolean>(false);\n\n typesForm = this.#fb.group({\n types: this.#fb.array([])\n });\n\n get typeArray(): FormArray {\n return this.typesForm.controls['types'] as FormArray;\n }\n\n getTypeForm(c: AbstractControl<any, any>): FormGroup {\n return c as FormGroup;\n }\n\n createTypeConfigObject() {\n this.busy.set(true);\n return this.#shellConfig\n .create(SHELL_CONFIG_TYPES, [])\n .pipe(\n tap((res) => {\n this.hasTypeConfig = !!res;\n this.#typeConfigObjectId = res.id;\n }),\n finalize(() => {\n this.busy.set(false);\n })\n )\n .subscribe();\n }\n\n addType() {\n this.typeArray.push(\n this.#fb.group({\n id: ['', Validators.required],\n baseType: [SystemType.DOCUMENT],\n instantApply: [false],\n applicableToMimeType: ['*/*']\n })\n );\n }\n\n removeType(idx: number) {\n this.typeArray.removeAt(idx);\n this.typesForm.markAsDirty();\n }\n\n #formToTypes(): ClientShellType[] {\n return this.typeArray.value.map((f: any) => {\n return {\n id: f.id,\n baseType: f.baseType,\n instantApply: f.instantApply,\n applicableToMimeTypes: [f.applicableToMimeType]\n };\n });\n }\n\n delete() {\n if (this.#typeConfigObjectId) {\n this.busy.set(true);\n this.#shellConfig\n .delete(this.#typeConfigObjectId)\n .pipe(\n tap(() => {\n this.typeArray.clear();\n this.types = [];\n this.hasTypeConfig = false;\n this.typesForm.markAsPristine();\n }),\n finalize(() => {\n this.busy.set(false);\n })\n )\n .subscribe();\n }\n }\n\n save() {\n this.busy.set(true);\n const types = this.#formToTypes();\n if (this.#typeConfigObjectId) {\n this.#shellConfig\n .update(this.#typeConfigObjectId, types)\n .pipe(\n finalize(() => {\n this.busy.set(false);\n })\n )\n .subscribe(() => {\n this.types = types;\n this.typesForm.markAsPristine();\n });\n }\n }\n\n export() {\n if (this.#typeConfigObjectId) {\n this.#shellConfig.download(this.#typeConfigObjectId, `${SHELL_CONFIG_TYPES}.json`);\n }\n }\n import() {\n let contentStreamId: string | undefined;\n if (this.#typeConfigObjectId)\n this.#shell\n .pickFile()\n .pipe(\n switchMap((file) => (file ? this.#shellConfig.upload(this.#typeConfigObjectId!, file) : of(null))),\n tap((x: DmsObject) => {\n if (x.content?.contentStreamId !== contentStreamId) this.#loadTypes();\n })\n )\n .subscribe();\n }\n\n #loadTypes() {\n this.#shellConfig.get(SHELL_CONFIG_TYPES).subscribe((typeConfig) => {\n this.hasTypeConfig = !!typeConfig;\n this.#typeConfigObjectId = typeConfig?.id;\n this.types = typeConfig ? (typeConfig.data as ClientShellType[]) : [];\n\n this.types.forEach((type) => {\n const applicableToMimeTypes = type.applicableToMimeTypes || [];\n this.typeArray.push(\n this.#fb.group({\n id: [type.id, Validators.required],\n baseType: [type.baseType],\n instantApply: [type.instantApply],\n applicableToMimeType: [applicableToMimeTypes[0] || '']\n })\n );\n });\n });\n }\n\n ngOnInit(): void {\n this.#loadTypes();\n }\n}\n","<header>\n <h2>\n {{ 'yuv.app.shell-admin.types.headline' | translate }}\n </h2>\n\n @if (hasTypeConfig) {\n <div class=\"export\">\n <button [disabled]=\"busy()\" mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.config.export.tooltip' | translate\" (click)=\"export()\">\n <mat-icon>download</mat-icon>\n </button>\n <button [disabled]=\"busy()\" mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.config.import.tooltip' | translate\" (click)=\"import()\">\n <mat-icon>upload</mat-icon>\n </button>\n </div>\n\n <div class=\"buttons\">\n <button [disabled]=\"busy()\" mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.types.config.add.tooltip' | translate\" (click)=\"addType()\"><mat-icon>add</mat-icon></button>\n <button [disabled]=\"typesForm.invalid || typesForm.pristine || busy()\" mat-flat-button (click)=\"save()\">\n {{ 'yuv.app.shell-admin.types.config.save' | translate }}\n </button>\n <button mat-icon-button [matTooltip]=\"'yuv.app.shell-admin.config.delete.tooltip' | translate\" [disabled]=\"busy()\" (click)=\"delete()\"><mat-icon>delete</mat-icon></button>\n </div>\n } @else {\n <button mat-flat-button [disabled]=\"busy()\" (click)=\"createTypeConfigObject()\">{{ 'yuv.app.shell-admin.types.config.create' | translate }}</button>\n }\n @if (busy()) {\n <mat-progress-bar mode=\"indeterminate\" class=\"progress-bar\"></mat-progress-bar>\n }\n</header>\n\n<form [formGroup]=\"typesForm\">\n <div formArrayName=\"types\">\n @for (typeForm of typeArray.controls; track $index) {\n <section class=\"type\" [formGroup]=\"getTypeForm(typeForm)\">\n <button mat-icon-button\n [matTooltip]=\"'yuv.app.shell-admin.types.config.item.delete.tooltip' | translate\"\n (click)=\"removeType($index)\"><mat-icon>delete</mat-icon></button>\n\n <mat-form-field appearance=\"outline\" class=\"id\">\n <mat-label>{{ 'yuv.app.shell-admin.type.config.id' | translate }}</mat-label>\n <input matInput formControlName=\"id\" />\n </mat-form-field>\n\n @let bt = getTypeForm(typeForm).get('baseType')?.value;\n @if (bt === 'system:document' || bt === 'system:object') {\n <mat-form-field class=\"mimetype\">\n <mat-label>{{ 'yuv.app.shell-admin.type.config.mimetype' | translate }}</mat-label>\n <input matInput formControlName=\"applicableToMimeType\" />\n </mat-form-field>\n }\n\n <div class=\"baseType\">\n <mat-radio-group formControlName=\"baseType\">\n <mat-radio-button value=\"system:document\">Document</mat-radio-button>\n <mat-radio-button value=\"system:folder\">Folder</mat-radio-button>\n <mat-radio-button value=\"system:object\">Both</mat-radio-button>\n </mat-radio-group>\n </div>\n\n <div class=\"flags\">\n <mat-checkbox formControlName=\"instantApply\">{{ 'yuv.app.shell-admin.type.config.instant-apply' | translate }}</mat-checkbox>\n <div class=\"explain\">{{ 'yuv.app.shell-admin.type.config.instant-apply.explain' | translate }}</div>\n </div>\n </section>\n }\n </div>\n</form>\n","import { Component, inject } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { RouterModule } from '@angular/router';\nimport { TranslateModule, YUV_USER, YuvUser } from '@yuuvis/client-core';\n\n@Component({\n selector: 'ymsa-shell-admin',\n imports: [CommonModule, RouterModule, TranslateModule],\n templateUrl: './shell-admin.component.html',\n styleUrl: './shell-admin.component.scss'\n})\nexport class ShellAdminComponent {\n // user token has to be present because this is a part of the\n // required bootstrap process\n user = inject<YuvUser>(YUV_USER);\n // only admins should be able to see this page\n isValidUser = this.user?.authorities.includes('YUUVIS_TENANT_ADMIN');\n}\n","<header>\n <a routerLink=\"./\">yM Shell Admin </a>\n <nav>\n <a routerLink=\"apps\" routerLinkActive=\"active\">{{ 'yuv.app.shell-admin.nav.apps' | translate }}</a>\n <a routerLink=\"types\" routerLinkActive=\"active\">{{ 'yuv.app.shell-admin.nav.types' | translate }}</a>\n </nav>\n \n</header>\n@if (isValidUser) {\n <main>\n <router-outlet></router-outlet>\n </main>\n} @else {\n <main class=\"invalid-user\">\n <p>{{ 'yuv.app.shell-admin.invalid-user' | translate }}</p>\n </main>\n}\n","import { Component } from '@angular/core';\nimport { CommonModule } from '@angular/common';\n\n@Component({\n selector: 'ymsa-home',\n imports: [CommonModule],\n templateUrl: './home.component.html',\n styleUrl: './home.component.scss'\n})\nexport class HomePageComponent {}\n","\n\n\n<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\" width=\"24px\" ><path d=\"M680-280q25 0 42.5-17.5T740-340q0-25-17.5-42.5T680-400q-25 0-42.5 17.5T620-340q0 25 17.5 42.5T680-280Zm0 120q31 0 57-14.5t42-38.5q-22-13-47-20t-52-7q-27 0-52 7t-47 20q16 24 42 38.5t57 14.5ZM480-80q-139-35-229.5-159.5T160-516v-244l320-120 320 120v227q-19-8-39-14.5t-41-9.5v-147l-240-90-240 90v188q0 47 12.5 94t35 89.5Q310-290 342-254t71 60q11 32 29 61t41 52q-1 0-1.5.5t-1.5.5Zm200 0q-83 0-141.5-58.5T480-280q0-83 58.5-141.5T680-480q83 0 141.5 58.5T880-280q0 83-58.5 141.5T680-80ZM480-494Z\"/></svg>\n","import { Route } from '@angular/router';\nimport { AppsPageComponent } from './pages/apps/apps.component';\nimport { TypesPageComponent } from './pages/types/types.component';\nimport { ShellAdminComponent } from './shell-admin.component';\nimport { HomePageComponent } from './pages/home/home.component';\n\nexport const FeatureAdminRoutes: Route[] = [\n { path: '', component: ShellAdminComponent, children: [\n { path: '', component: HomePageComponent },\n { path: 'apps', component: AppsPageComponent },\n { path: 'types', component: TypesPageComponent },\n ] },\n];\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["i2","i4","i5","i6","TranslateModule","i8","i9"],"mappings":"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"}
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './lib/lib.routes';
@@ -0,0 +1,2 @@
1
+ import { Route } from '@angular/router';
2
+ export declare const FeatureAdminRoutes: Route[];
@@ -0,0 +1,39 @@
1
+ import { AbstractControl, FormArray, FormGroup } from '@angular/forms';
2
+ import { YuvUser } from '@yuuvis/client-core';
3
+ import { AutocompleteItem } from '@yuuvis/client-framework/autocomplete';
4
+ import { ClientShellFeature } from '@yuuvis/client-shell-core';
5
+ import { TmRole, TmUser } from '../../services/apps/apps.interface';
6
+ import * as i0 from "@angular/core";
7
+ export declare class AppsPageComponent {
8
+ #private;
9
+ user: YuvUser;
10
+ isValidUser: boolean;
11
+ users: TmUser[];
12
+ mappedUsers: {
13
+ features: ClientShellFeature[];
14
+ user: TmUser;
15
+ }[];
16
+ roles: TmRole[];
17
+ features: ClientShellFeature[];
18
+ selectedUser?: TmUser;
19
+ autocompleteValues: AutocompleteItem[];
20
+ busy: import("@angular/core").WritableSignal<boolean>;
21
+ loadingUsers: import("@angular/core").WritableSignal<boolean>;
22
+ featuresForm: FormGroup<{
23
+ features: FormArray<import("@angular/forms").FormControl<unknown>>;
24
+ }>;
25
+ get featureArray(): FormArray;
26
+ getFeatureForm(c: AbstractControl<any, any>): FormGroup;
27
+ hasFeatureConfig: boolean;
28
+ constructor();
29
+ createFeatureConfigObject(): void;
30
+ addFeature(): void;
31
+ removeFeature(idx: number): void;
32
+ fetchRoles(t: string): void;
33
+ delete(): void;
34
+ save(): void;
35
+ export(): void;
36
+ import(): void;
37
+ static ɵfac: i0.ɵɵFactoryDeclaration<AppsPageComponent, never>;
38
+ static ɵcmp: i0.ɵɵComponentDeclaration<AppsPageComponent, "ysfa-apps", never, {}, {}, never, never, true, never>;
39
+ }
@@ -0,0 +1,5 @@
1
+ import * as i0 from "@angular/core";
2
+ export declare class HomePageComponent {
3
+ static ɵfac: i0.ɵɵFactoryDeclaration<HomePageComponent, never>;
4
+ static ɵcmp: i0.ɵɵComponentDeclaration<HomePageComponent, "ymsa-home", never, {}, {}, never, never, true, never>;
5
+ }
@@ -0,0 +1,25 @@
1
+ import { OnInit } from '@angular/core';
2
+ import { AbstractControl, FormArray, FormGroup } from '@angular/forms';
3
+ import { ClientShellType } from '@yuuvis/client-shell-core';
4
+ import * as i0 from "@angular/core";
5
+ export declare class TypesPageComponent implements OnInit {
6
+ #private;
7
+ hasTypeConfig: boolean;
8
+ types: ClientShellType[];
9
+ busy: import("@angular/core").WritableSignal<boolean>;
10
+ typesForm: FormGroup<{
11
+ types: FormArray<import("@angular/forms").FormControl<unknown>>;
12
+ }>;
13
+ get typeArray(): FormArray;
14
+ getTypeForm(c: AbstractControl<any, any>): FormGroup;
15
+ createTypeConfigObject(): import("rxjs").Subscription;
16
+ addType(): void;
17
+ removeType(idx: number): void;
18
+ delete(): void;
19
+ save(): void;
20
+ export(): void;
21
+ import(): void;
22
+ ngOnInit(): void;
23
+ static ɵfac: i0.ɵɵFactoryDeclaration<TypesPageComponent, never>;
24
+ static ɵcmp: i0.ɵɵComponentDeclaration<TypesPageComponent, "ymsa-types", never, {}, {}, never, never, true, never>;
25
+ }
@@ -0,0 +1,15 @@
1
+ export interface TmUser {
2
+ id: string;
3
+ username: string;
4
+ createdTimestamp: number;
5
+ email: string;
6
+ enabled: boolean;
7
+ firstName: string;
8
+ lastName: string;
9
+ groups: string[];
10
+ roles: string[];
11
+ }
12
+ export interface TmRole {
13
+ name: string;
14
+ description: string;
15
+ }
@@ -0,0 +1,10 @@
1
+ import { Observable } from 'rxjs';
2
+ import { TmRole, TmUser } from './apps.interface';
3
+ import * as i0 from "@angular/core";
4
+ export declare class AppsService {
5
+ #private;
6
+ getUsers(): Observable<TmUser[]>;
7
+ getRoles(): Observable<TmRole[]>;
8
+ static ɵfac: i0.ɵɵFactoryDeclaration<AppsService, never>;
9
+ static ɵprov: i0.ɵɵInjectableDeclaration<AppsService>;
10
+ }
@@ -0,0 +1,8 @@
1
+ import { YuvUser } from '@yuuvis/client-core';
2
+ import * as i0 from "@angular/core";
3
+ export declare class ShellAdminComponent {
4
+ user: YuvUser;
5
+ isValidUser: boolean;
6
+ static ɵfac: i0.ɵɵFactoryDeclaration<ShellAdminComponent, never>;
7
+ static ɵcmp: i0.ɵɵComponentDeclaration<ShellAdminComponent, "ymsa-shell-admin", never, {}, {}, never, never, true, never>;
8
+ }
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@yuuvis/app-shell-admin",
3
+ "version": "2.0.0-beta.0",
4
+ "peerDependencies": {
5
+ "@angular/common": "^19.2.1",
6
+ "@angular/core": "^19.2.1"
7
+ },
8
+ "sideEffects": false,
9
+ "module": "fesm2022/yuuvis-app-shell-admin.mjs",
10
+ "typings": "index.d.ts",
11
+ "exports": {
12
+ "./package.json": {
13
+ "default": "./package.json"
14
+ },
15
+ ".": {
16
+ "types": "./index.d.ts",
17
+ "default": "./fesm2022/yuuvis-app-shell-admin.mjs"
18
+ }
19
+ },
20
+ "dependencies": {
21
+ "tslib": "^2.3.0"
22
+ }
23
+ }