@yuuvis/client-shell 2.0.15 → 2.1.1
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 +51 -1
- package/fesm2022/yuuvis-client-shell-settings.component-ANbgn9LN.mjs +107 -0
- package/fesm2022/yuuvis-client-shell-settings.component-ANbgn9LN.mjs.map +1 -0
- package/fesm2022/yuuvis-client-shell.mjs +309 -194
- package/fesm2022/yuuvis-client-shell.mjs.map +1 -1
- package/index.d.ts +6 -4
- package/lib/assets/i18n/de.json +1 -0
- package/lib/assets/i18n/en.json +1 -0
- package/lib/client-shell.component.d.ts +3 -0
- package/lib/client-shell.module.d.ts +7 -0
- package/lib/components/app-header/app-header-slot.directive.d.ts +10 -0
- package/lib/components/app-header/app-header.component.d.ts +12 -0
- package/lib/components/shell-logo/shell-logo.component.d.ts +5 -0
- package/lib/components/sidebar-nav/sidebar-nav.component.d.ts +1 -1
- package/lib/services/client-shell/client-shell.service.d.ts +16 -0
- package/package.json +4 -4
- package/fesm2022/yuuvis-client-shell-settings.component-Dyr0I9Gx.mjs +0 -107
- package/fesm2022/yuuvis-client-shell-settings.component-Dyr0I9Gx.mjs.map +0 -1
- package/lib/components/app-logo/app-logo.component.d.ts +0 -7
- /package/lib/services/{shell-widgets.service.d.ts → shell-widgets/shell-widgets.service.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -91,4 +91,54 @@ const flavor = {
|
|
|
91
91
|
mimeTypes: ['*/*']
|
|
92
92
|
}
|
|
93
93
|
};
|
|
94
|
-
```
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## App header
|
|
97
|
+
|
|
98
|
+
The shell provides the ability to use the integrated app header. This header provides a best practice approach for apps to apply a unified header pattern across apps that is aimed towards supporting usability within a shell client.
|
|
99
|
+
|
|
100
|
+
Activating the app header will add a header component to the shell offering various slots to be populated with semantic components. This will guarranty a unified look across all applications.
|
|
101
|
+
|
|
102
|
+
To enable app header set `appHeader` property of your apps `yuv-manifest.json` to `true`:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"id": "io.yuuvis.app.drive",
|
|
107
|
+
"title": "yM Drive",
|
|
108
|
+
"ui": {
|
|
109
|
+
"path": "drive",
|
|
110
|
+
"iconName": "cloud_circle",
|
|
111
|
+
"routes": "YuuvisDriveRoutes"
|
|
112
|
+
},
|
|
113
|
+
"extension": "AppDriveExtension",
|
|
114
|
+
"appHeader": true
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
You might also change the clients routes array:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
// apps used in the client shell
|
|
122
|
+
export const apps: App[] = [
|
|
123
|
+
{
|
|
124
|
+
id: 'io.yuuvis.app.example',
|
|
125
|
+
title: 'Example App',
|
|
126
|
+
path: 'example',
|
|
127
|
+
iconName: 'home',
|
|
128
|
+
loadChildren: () => import('desktops').then((m) => m.ExampleRoutes),
|
|
129
|
+
options: {
|
|
130
|
+
appHeader: true
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### provide the slots
|
|
137
|
+
Anywhere within your app you could set components to be shown in one of the app headers slots.
|
|
138
|
+
|
|
139
|
+
```html
|
|
140
|
+
<!-- show ymd-drive-search in app headers search slot -->
|
|
141
|
+
<ng-template yuvAppHeaderSlot="search"><ymd-drive-search></ymd-drive-search></ng-template>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Currently there are 3 slots available: `actions`, `search`, `notifications`. Make sure to import `YuvClientShellModule` in the component that sets the slots, because this module exposes the `yuvAppHeaderSlot` directive.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, signal, Component } from '@angular/core';
|
|
3
|
+
import { toSignal } from '@angular/core/rxjs-interop';
|
|
4
|
+
import * as i1 from '@yuuvis/client-core';
|
|
5
|
+
import { UserService, ConfigService, TranslateService, AppCacheService, TranslateModule } from '@yuuvis/client-core';
|
|
6
|
+
import { DOCUMENT, CommonModule } from '@angular/common';
|
|
7
|
+
import { HttpClient } from '@angular/common/http';
|
|
8
|
+
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
|
9
|
+
import { MatButtonToggleGroup, MatButtonToggle } from '@angular/material/button-toggle';
|
|
10
|
+
import * as i2 from '@angular/material/expansion';
|
|
11
|
+
import { MatExpansionModule } from '@angular/material/expansion';
|
|
12
|
+
import { MatSelectModule } from '@angular/material/select';
|
|
13
|
+
import * as i3 from '@angular/material/table';
|
|
14
|
+
import { MatTableModule } from '@angular/material/table';
|
|
15
|
+
import { ShellService } from '@yuuvis/client-shell-core';
|
|
16
|
+
import { map } from 'rxjs';
|
|
17
|
+
|
|
18
|
+
class SettingsPageComponent {
|
|
19
|
+
constructor() {
|
|
20
|
+
this.userService = inject(UserService);
|
|
21
|
+
this.config = inject(ConfigService);
|
|
22
|
+
this.translate = inject(TranslateService);
|
|
23
|
+
this.shell = inject(ShellService);
|
|
24
|
+
this.document = inject(DOCUMENT);
|
|
25
|
+
this.#appCache = inject(AppCacheService);
|
|
26
|
+
this.#http = inject(HttpClient);
|
|
27
|
+
this.#fb = inject(FormBuilder);
|
|
28
|
+
this.clientLocales = signal([]);
|
|
29
|
+
this.clientVersion = signal(undefined);
|
|
30
|
+
this.clientAboutData = signal(undefined);
|
|
31
|
+
this.user = toSignal(this.userService.user$);
|
|
32
|
+
this.appSettingForms$ = this.shell.appSettings$.pipe(map((settings) => settings.map((e) => {
|
|
33
|
+
const x = {};
|
|
34
|
+
const fcn = e.properties.map((p) => ({
|
|
35
|
+
label: this.translate.instant(p.label) || p.label,
|
|
36
|
+
name: p.name,
|
|
37
|
+
type: p.type
|
|
38
|
+
}));
|
|
39
|
+
e.properties.forEach((p) => {
|
|
40
|
+
x[p.name] = [p.value];
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
appID: e.appID,
|
|
44
|
+
label: e.label,
|
|
45
|
+
formControls: fcn,
|
|
46
|
+
form: this.#fb.group(x)
|
|
47
|
+
};
|
|
48
|
+
})));
|
|
49
|
+
}
|
|
50
|
+
#appCache;
|
|
51
|
+
#http;
|
|
52
|
+
#fb;
|
|
53
|
+
saveAppSettings(appID, form) {
|
|
54
|
+
this.userService
|
|
55
|
+
.saveUserSettings({
|
|
56
|
+
clientAppSettings: {
|
|
57
|
+
[appID]: form.value
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
.subscribe({
|
|
61
|
+
next: () => {
|
|
62
|
+
form.markAsPristine();
|
|
63
|
+
},
|
|
64
|
+
error: (err) => {
|
|
65
|
+
console.error('Error saving app settings', err);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
changeClientLocale(iso) {
|
|
70
|
+
this.userService.changeClientLocale(iso);
|
|
71
|
+
}
|
|
72
|
+
toggleDarkMode() {
|
|
73
|
+
const root = document.getElementsByTagName('body')[0];
|
|
74
|
+
root.classList.toggle('dark');
|
|
75
|
+
}
|
|
76
|
+
ngOnInit() {
|
|
77
|
+
this.clientVersion.set(this.document.body.getAttribute('data-version') ?? 'dev');
|
|
78
|
+
this.clientLocales.set(this.config.getClientLocales());
|
|
79
|
+
this.getAboutData();
|
|
80
|
+
}
|
|
81
|
+
getAboutData() {
|
|
82
|
+
this.#http
|
|
83
|
+
.get('assets/about.data.json')
|
|
84
|
+
.pipe(map((response) => ({
|
|
85
|
+
...response,
|
|
86
|
+
libraries: response.libraries
|
|
87
|
+
? response.libraries.map((lib) => ({
|
|
88
|
+
...lib,
|
|
89
|
+
...(lib.license !== 'SEE LICENSE IN LICENSE' ? { link: `https://opensource.org/license/${lib.license}` } : {})
|
|
90
|
+
}))
|
|
91
|
+
: undefined
|
|
92
|
+
})))
|
|
93
|
+
.subscribe({
|
|
94
|
+
next: (response) => this.clientAboutData.set(response),
|
|
95
|
+
error: (error) => console.log({ error })
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.12", ngImport: i0, type: SettingsPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
99
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.12", type: SettingsPageComponent, isStandalone: true, selector: "yuv-settings", ngImport: i0, template: "<header>\n <h1>{{ 'yuv.shell.settings.title' | translate }}</h1>\n <span class=\"subhead\">{{ 'yuv.shell.settings.client.version' | translate }}: {{ clientVersion() }}</span>\n</header>\n\n<main>\n <div class=\"content-container\">\n <!-- User Info -->\n @let user = this.user();\n @if (user) {\n <section class=\"section user\">\n <h2 class=\"section__title\">{{ user.title }}</h2>\n <div class=\"section__body user__data grid\">\n <div class=\"user__data-entry grid-row\">\n <span class=\"user__data-entry-key grid-col grid-col-key\"> {{ 'yuv.shell.settings.user.email' | translate }}: </span>\n <span class=\"user__data-entry-value grid-col grid-col-value\">\n {{ user.email }}\n </span>\n </div>\n <div class=\"user__data-entry grid-row\">\n <span class=\"user__data-entry-key grid-col grid-col-key\"> {{ 'yuv.shell.settings.tenant' | translate }}: </span>\n <span class=\"user__data-entry-value grid-col grid-col-value\">\n {{ user.tenant }}\n </span>\n </div>\n\n <!--<div class=\"user__data\">{{ 'yuv.shell.settings.user.name' | translate }}: {{ user.username }}</div>\n <div class=\"user__data-entry\">{{ 'yuv.shell.settings.user.email' | translate }}: {{ user.email }}</div>\n <div class=\"user__data-entry\">{{ 'yuv.shell.settings.tenant' | translate }}: {{ user.tenant }}</div>-->\n </div>\n </section>\n }\n <!-- Language Settings -->\n <section yuvOfflineDisabled class=\"section\">\n <h2 class=\"section__title\">{{ 'yuv.shell.settings.language' | translate }}</h2>\n <div class=\"section__body\">\n <mat-button-toggle-group\n name=\"language\"\n [value]=\"translate.currentLang\"\n (valueChange)=\"changeClientLocale($event)\"\n [attr.aria-label]=\"'yuv.shell.settings.language' | translate\"\n >\n @for (locale of clientLocales(); track locale.iso) {\n <mat-button-toggle value=\"{{ locale.iso }}\">{{ locale.label }}</mat-button-toggle>\n }\n </mat-button-toggle-group>\n </div>\n </section>\n\n <!-- Mode -->\n <section class=\"section\">\n <h2 class=\"section__title\">{{ 'yuv.shell.settings.mode' | translate }}</h2>\n <div class=\"section__body\">\n <mat-button-toggle-group\n name=\"mode\"\n [value]=\"translate.currentLang\"\n (valueChange)=\"changeClientLocale($event)\"\n [attr.aria-label]=\"'yuv.shell.settings.language' | translate\"\n >\n @for (locale of clientLocales(); track locale.iso) {\n <mat-button-toggle value=\"{{ locale.iso }}\">{{ locale.label }}</mat-button-toggle>\n }\n </mat-button-toggle-group>\n </div>\n </section>\n\n\n <!-- About Info -->\n @let libraries = this.clientAboutData()?.libraries;\n @if (libraries) {\n <section class=\"section about\">\n <h2 class=\"section__title\">{{ 'yuv.shell.settings.about.title' | translate }}</h2>\n <mat-expansion-panel class=\"about__panel\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ 'yuv.shell.settings.dependency-info.title' | translate }}</mat-panel-title>\n <!-- <mat-panel-description>{{ '' | translate }}</mat-panel-description>-->\n </mat-expansion-panel-header>\n\n <ng-template matExpansionPanelContent>\n <div class=\"about__panel-content\">\n <table mat-table [dataSource]=\"libraries\" class=\"mat-elevation-z8\">\n <!-- Name Column -->\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef>{{ 'yuv.shell.settings.dependency-info.package' | translate }}</th>\n <td mat-cell *matCellDef=\"let element\">{{ element.name }}</td>\n </ng-container>\n\n <!-- Version Column -->\n <ng-container matColumnDef=\"version\">\n <th mat-header-cell *matHeaderCellDef>{{ 'yuv.shell.settings.dependency-info.version' | translate }}</th>\n <td mat-cell *matCellDef=\"let element\">{{ element.version }}</td>\n </ng-container>\n\n <!-- License Column -->\n <ng-container matColumnDef=\"license\">\n <th mat-header-cell *matHeaderCellDef>{{ 'yuv.shell.settings.dependency-info.license' | translate }}</th>\n <td mat-cell *matCellDef=\"let element\">\n @if (element.link) {\n <a href=\"{{ element.link }}\" target=\"_blank\">{{ element.license }}</a>\n } @else {\n {{ element.license }}\n }\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"['name', 'version', 'license']\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: ['name', 'version', 'license']\"></tr>\n </table>\n </div>\n </ng-template>\n </mat-expansion-panel>\n </section>\n }\n <!-- app settings -->\n <!-- TODO: activate one feature is refined -->\n <!-- @for (c of appSettingForms$ | async; track $index) {\n <section>\n {{ c.label }}\n <form [formGroup]=\"c.form\" (ngSubmit)=\"saveAppSettings(c.appID, c.form)\">\n @for (n of c.formControls; track $index) {\n <label\n >{{ n.label }}\n\n @switch (n.type) {\n @case ('string') {\n <input type=\"text\" [formControlName]=\"n.name\" />\n }\n @case ('number') {\n <input type=\"number\" [formControlName]=\"n.name\" />\n }\n }\n </label>\n }\n <button [ngClass]=\"{ hideen: c.form.untouched }\" [disabled]=\"c.form.invalid\">Save</button>\n </form>\n </section>\n } -->\n </div>\n</main>\n", styles: [":host{display:grid;grid-template-columns:1fr auto;grid-template-rows:auto 1fr;grid-template-areas:\"header\" \"settings\";height:100%;overflow:hidden;overflow-y:auto}:host header{grid-area:header;padding:var(--ymt-spacing-3xl)}:host header h1{margin:0}:host header .subhead{font:var(--ymt-font-subhead);color:var(--ymt-text-color-subtle)}:host main{grid-area:settings;padding:var(--ymt-spacing-m);padding-right:0;overflow-y:auto;scrollbar-gutter:stable}:host main .content-container{max-width:900px}:host main section{display:flex;flex-flow:column;padding:var(--ymt-spacing-m);margin-bottom:var(--ymt-spacing-l)}:host main .about__panel-content{overflow-x:auto}:host main .grid-row{display:grid;grid-template-columns:minmax(10ch,1fr) 11fr;grid-gap:var(--ymt-spacing-s);grid-template-areas:\"key value\";align-items:baseline}:host main .grid-col-key{font-weight:700;grid-area:key}:host main .grid-col-value{grid-area:value;word-break:break-all}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i2.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i2.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i2.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i2.MatExpansionPanelContent, selector: "ng-template[matExpansionPanelContent]" }, { kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i3.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i3.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i3.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i3.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i3.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i3.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i3.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i3.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i3.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i3.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }] }); }
|
|
100
|
+
}
|
|
101
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.12", ngImport: i0, type: SettingsPageComponent, decorators: [{
|
|
102
|
+
type: Component,
|
|
103
|
+
args: [{ selector: 'yuv-settings', standalone: true, imports: [CommonModule, TranslateModule, MatSelectModule, ReactiveFormsModule, MatButtonToggleGroup, MatButtonToggle, MatExpansionModule, MatTableModule], template: "<header>\n <h1>{{ 'yuv.shell.settings.title' | translate }}</h1>\n <span class=\"subhead\">{{ 'yuv.shell.settings.client.version' | translate }}: {{ clientVersion() }}</span>\n</header>\n\n<main>\n <div class=\"content-container\">\n <!-- User Info -->\n @let user = this.user();\n @if (user) {\n <section class=\"section user\">\n <h2 class=\"section__title\">{{ user.title }}</h2>\n <div class=\"section__body user__data grid\">\n <div class=\"user__data-entry grid-row\">\n <span class=\"user__data-entry-key grid-col grid-col-key\"> {{ 'yuv.shell.settings.user.email' | translate }}: </span>\n <span class=\"user__data-entry-value grid-col grid-col-value\">\n {{ user.email }}\n </span>\n </div>\n <div class=\"user__data-entry grid-row\">\n <span class=\"user__data-entry-key grid-col grid-col-key\"> {{ 'yuv.shell.settings.tenant' | translate }}: </span>\n <span class=\"user__data-entry-value grid-col grid-col-value\">\n {{ user.tenant }}\n </span>\n </div>\n\n <!--<div class=\"user__data\">{{ 'yuv.shell.settings.user.name' | translate }}: {{ user.username }}</div>\n <div class=\"user__data-entry\">{{ 'yuv.shell.settings.user.email' | translate }}: {{ user.email }}</div>\n <div class=\"user__data-entry\">{{ 'yuv.shell.settings.tenant' | translate }}: {{ user.tenant }}</div>-->\n </div>\n </section>\n }\n <!-- Language Settings -->\n <section yuvOfflineDisabled class=\"section\">\n <h2 class=\"section__title\">{{ 'yuv.shell.settings.language' | translate }}</h2>\n <div class=\"section__body\">\n <mat-button-toggle-group\n name=\"language\"\n [value]=\"translate.currentLang\"\n (valueChange)=\"changeClientLocale($event)\"\n [attr.aria-label]=\"'yuv.shell.settings.language' | translate\"\n >\n @for (locale of clientLocales(); track locale.iso) {\n <mat-button-toggle value=\"{{ locale.iso }}\">{{ locale.label }}</mat-button-toggle>\n }\n </mat-button-toggle-group>\n </div>\n </section>\n\n <!-- Mode -->\n <section class=\"section\">\n <h2 class=\"section__title\">{{ 'yuv.shell.settings.mode' | translate }}</h2>\n <div class=\"section__body\">\n <mat-button-toggle-group\n name=\"mode\"\n [value]=\"translate.currentLang\"\n (valueChange)=\"changeClientLocale($event)\"\n [attr.aria-label]=\"'yuv.shell.settings.language' | translate\"\n >\n @for (locale of clientLocales(); track locale.iso) {\n <mat-button-toggle value=\"{{ locale.iso }}\">{{ locale.label }}</mat-button-toggle>\n }\n </mat-button-toggle-group>\n </div>\n </section>\n\n\n <!-- About Info -->\n @let libraries = this.clientAboutData()?.libraries;\n @if (libraries) {\n <section class=\"section about\">\n <h2 class=\"section__title\">{{ 'yuv.shell.settings.about.title' | translate }}</h2>\n <mat-expansion-panel class=\"about__panel\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ 'yuv.shell.settings.dependency-info.title' | translate }}</mat-panel-title>\n <!-- <mat-panel-description>{{ '' | translate }}</mat-panel-description>-->\n </mat-expansion-panel-header>\n\n <ng-template matExpansionPanelContent>\n <div class=\"about__panel-content\">\n <table mat-table [dataSource]=\"libraries\" class=\"mat-elevation-z8\">\n <!-- Name Column -->\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef>{{ 'yuv.shell.settings.dependency-info.package' | translate }}</th>\n <td mat-cell *matCellDef=\"let element\">{{ element.name }}</td>\n </ng-container>\n\n <!-- Version Column -->\n <ng-container matColumnDef=\"version\">\n <th mat-header-cell *matHeaderCellDef>{{ 'yuv.shell.settings.dependency-info.version' | translate }}</th>\n <td mat-cell *matCellDef=\"let element\">{{ element.version }}</td>\n </ng-container>\n\n <!-- License Column -->\n <ng-container matColumnDef=\"license\">\n <th mat-header-cell *matHeaderCellDef>{{ 'yuv.shell.settings.dependency-info.license' | translate }}</th>\n <td mat-cell *matCellDef=\"let element\">\n @if (element.link) {\n <a href=\"{{ element.link }}\" target=\"_blank\">{{ element.license }}</a>\n } @else {\n {{ element.license }}\n }\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"['name', 'version', 'license']\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: ['name', 'version', 'license']\"></tr>\n </table>\n </div>\n </ng-template>\n </mat-expansion-panel>\n </section>\n }\n <!-- app settings -->\n <!-- TODO: activate one feature is refined -->\n <!-- @for (c of appSettingForms$ | async; track $index) {\n <section>\n {{ c.label }}\n <form [formGroup]=\"c.form\" (ngSubmit)=\"saveAppSettings(c.appID, c.form)\">\n @for (n of c.formControls; track $index) {\n <label\n >{{ n.label }}\n\n @switch (n.type) {\n @case ('string') {\n <input type=\"text\" [formControlName]=\"n.name\" />\n }\n @case ('number') {\n <input type=\"number\" [formControlName]=\"n.name\" />\n }\n }\n </label>\n }\n <button [ngClass]=\"{ hideen: c.form.untouched }\" [disabled]=\"c.form.invalid\">Save</button>\n </form>\n </section>\n } -->\n </div>\n</main>\n", styles: [":host{display:grid;grid-template-columns:1fr auto;grid-template-rows:auto 1fr;grid-template-areas:\"header\" \"settings\";height:100%;overflow:hidden;overflow-y:auto}:host header{grid-area:header;padding:var(--ymt-spacing-3xl)}:host header h1{margin:0}:host header .subhead{font:var(--ymt-font-subhead);color:var(--ymt-text-color-subtle)}:host main{grid-area:settings;padding:var(--ymt-spacing-m);padding-right:0;overflow-y:auto;scrollbar-gutter:stable}:host main .content-container{max-width:900px}:host main section{display:flex;flex-flow:column;padding:var(--ymt-spacing-m);margin-bottom:var(--ymt-spacing-l)}:host main .about__panel-content{overflow-x:auto}:host main .grid-row{display:grid;grid-template-columns:minmax(10ch,1fr) 11fr;grid-gap:var(--ymt-spacing-s);grid-template-areas:\"key value\";align-items:baseline}:host main .grid-col-key{font-weight:700;grid-area:key}:host main .grid-col-value{grid-area:value;word-break:break-all}\n"] }]
|
|
104
|
+
}] });
|
|
105
|
+
|
|
106
|
+
export { SettingsPageComponent };
|
|
107
|
+
//# sourceMappingURL=yuuvis-client-shell-settings.component-ANbgn9LN.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yuuvis-client-shell-settings.component-ANbgn9LN.mjs","sources":["../../../../../libs/yuuvis/client-shell/src/lib/pages/settings/settings.component.ts","../../../../../libs/yuuvis/client-shell/src/lib/pages/settings/settings.component.html"],"sourcesContent":["import { Component, inject, OnInit, Signal, signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { AppCacheService, ConfigService, TranslateModule, TranslateService, UserService, YuvConfigLanguages, YuvUser } from '@yuuvis/client-core';\n\nimport { CommonModule, DOCUMENT } from '@angular/common';\nimport { HttpClient } from '@angular/common/http';\nimport { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';\nimport { MatButtonToggle, MatButtonToggleGroup } from '@angular/material/button-toggle';\nimport { MatExpansionModule } from '@angular/material/expansion';\nimport { MatSelectModule } from '@angular/material/select';\nimport { MatTableModule } from '@angular/material/table';\nimport { ShellAppSettingProperty, ShellAppSettings, ShellService } from '@yuuvis/client-shell-core';\nimport { map, Observable } from 'rxjs';\nimport { AboutData } from './settings.model';\n\n@Component({\n selector: 'yuv-settings',\n standalone: true,\n imports: [CommonModule, TranslateModule, MatSelectModule, ReactiveFormsModule, MatButtonToggleGroup, MatButtonToggle, MatExpansionModule, MatTableModule],\n templateUrl: './settings.component.html',\n styleUrls: ['./settings.component.scss']\n})\nexport class SettingsPageComponent implements OnInit {\n private userService = inject(UserService);\n private config = inject(ConfigService);\n public translate = inject(TranslateService);\n private shell = inject(ShellService);\n readonly document = inject(DOCUMENT);\n #appCache = inject(AppCacheService);\n #http = inject(HttpClient);\n\n #fb = inject(FormBuilder);\n\n clientLocales = signal<YuvConfigLanguages[]>([]);\n clientVersion = signal<string | undefined>(undefined);\n clientAboutData = signal<AboutData | undefined>(undefined);\n\n user: Signal<YuvUser | undefined> = toSignal(this.userService.user$);\n appSettingForms$: Observable<\n {\n appID: string;\n label: string;\n formControls: ShellAppSettingProperty[];\n form: FormGroup;\n }[]\n > = this.shell.appSettings$.pipe(\n map((settings: ShellAppSettings[]) =>\n settings.map((e) => {\n const x: any = {};\n const fcn: ShellAppSettingProperty[] = e.properties.map((p) => ({\n label: this.translate.instant(p.label) || p.label,\n name: p.name,\n type: p.type\n }));\n e.properties.forEach((p) => {\n x[p.name] = [p.value];\n });\n return {\n appID: e.appID,\n label: e.label,\n formControls: fcn,\n form: this.#fb.group(x)\n };\n })\n )\n );\n\n saveAppSettings(appID: string, form: FormGroup) {\n this.userService\n .saveUserSettings({\n clientAppSettings: {\n [appID]: form.value\n }\n })\n .subscribe({\n next: () => {\n form.markAsPristine();\n },\n error: (err) => {\n console.error('Error saving app settings', err);\n }\n });\n }\n\n changeClientLocale(iso: string) {\n this.userService.changeClientLocale(iso);\n }\n\n toggleDarkMode() {\n const root = document.getElementsByTagName('body')[0];\n root.classList.toggle('dark');\n }\n\n ngOnInit(): void {\n this.clientVersion.set(this.document.body.getAttribute('data-version') ?? 'dev');\n this.clientLocales.set(this.config.getClientLocales());\n this.getAboutData();\n }\n\n getAboutData() {\n this.#http\n .get('assets/about.data.json')\n .pipe(\n map((response: AboutData) => ({\n ...response,\n libraries: response.libraries\n ? response.libraries.map((lib) => ({\n ...lib,\n ...(lib.license !== 'SEE LICENSE IN LICENSE' ? { link: `https://opensource.org/license/${lib.license}` } : {})\n }))\n : undefined\n }))\n )\n .subscribe({\n next: (response: AboutData) => this.clientAboutData.set(response),\n error: (error) => console.log({ error })\n });\n }\n}\n","<header>\n <h1>{{ 'yuv.shell.settings.title' | translate }}</h1>\n <span class=\"subhead\">{{ 'yuv.shell.settings.client.version' | translate }}: {{ clientVersion() }}</span>\n</header>\n\n<main>\n <div class=\"content-container\">\n <!-- User Info -->\n @let user = this.user();\n @if (user) {\n <section class=\"section user\">\n <h2 class=\"section__title\">{{ user.title }}</h2>\n <div class=\"section__body user__data grid\">\n <div class=\"user__data-entry grid-row\">\n <span class=\"user__data-entry-key grid-col grid-col-key\"> {{ 'yuv.shell.settings.user.email' | translate }}: </span>\n <span class=\"user__data-entry-value grid-col grid-col-value\">\n {{ user.email }}\n </span>\n </div>\n <div class=\"user__data-entry grid-row\">\n <span class=\"user__data-entry-key grid-col grid-col-key\"> {{ 'yuv.shell.settings.tenant' | translate }}: </span>\n <span class=\"user__data-entry-value grid-col grid-col-value\">\n {{ user.tenant }}\n </span>\n </div>\n\n <!--<div class=\"user__data\">{{ 'yuv.shell.settings.user.name' | translate }}: {{ user.username }}</div>\n <div class=\"user__data-entry\">{{ 'yuv.shell.settings.user.email' | translate }}: {{ user.email }}</div>\n <div class=\"user__data-entry\">{{ 'yuv.shell.settings.tenant' | translate }}: {{ user.tenant }}</div>-->\n </div>\n </section>\n }\n <!-- Language Settings -->\n <section yuvOfflineDisabled class=\"section\">\n <h2 class=\"section__title\">{{ 'yuv.shell.settings.language' | translate }}</h2>\n <div class=\"section__body\">\n <mat-button-toggle-group\n name=\"language\"\n [value]=\"translate.currentLang\"\n (valueChange)=\"changeClientLocale($event)\"\n [attr.aria-label]=\"'yuv.shell.settings.language' | translate\"\n >\n @for (locale of clientLocales(); track locale.iso) {\n <mat-button-toggle value=\"{{ locale.iso }}\">{{ locale.label }}</mat-button-toggle>\n }\n </mat-button-toggle-group>\n </div>\n </section>\n\n <!-- Mode -->\n <section class=\"section\">\n <h2 class=\"section__title\">{{ 'yuv.shell.settings.mode' | translate }}</h2>\n <div class=\"section__body\">\n <mat-button-toggle-group\n name=\"mode\"\n [value]=\"translate.currentLang\"\n (valueChange)=\"changeClientLocale($event)\"\n [attr.aria-label]=\"'yuv.shell.settings.language' | translate\"\n >\n @for (locale of clientLocales(); track locale.iso) {\n <mat-button-toggle value=\"{{ locale.iso }}\">{{ locale.label }}</mat-button-toggle>\n }\n </mat-button-toggle-group>\n </div>\n </section>\n\n\n <!-- About Info -->\n @let libraries = this.clientAboutData()?.libraries;\n @if (libraries) {\n <section class=\"section about\">\n <h2 class=\"section__title\">{{ 'yuv.shell.settings.about.title' | translate }}</h2>\n <mat-expansion-panel class=\"about__panel\">\n <mat-expansion-panel-header>\n <mat-panel-title>{{ 'yuv.shell.settings.dependency-info.title' | translate }}</mat-panel-title>\n <!-- <mat-panel-description>{{ '' | translate }}</mat-panel-description>-->\n </mat-expansion-panel-header>\n\n <ng-template matExpansionPanelContent>\n <div class=\"about__panel-content\">\n <table mat-table [dataSource]=\"libraries\" class=\"mat-elevation-z8\">\n <!-- Name Column -->\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef>{{ 'yuv.shell.settings.dependency-info.package' | translate }}</th>\n <td mat-cell *matCellDef=\"let element\">{{ element.name }}</td>\n </ng-container>\n\n <!-- Version Column -->\n <ng-container matColumnDef=\"version\">\n <th mat-header-cell *matHeaderCellDef>{{ 'yuv.shell.settings.dependency-info.version' | translate }}</th>\n <td mat-cell *matCellDef=\"let element\">{{ element.version }}</td>\n </ng-container>\n\n <!-- License Column -->\n <ng-container matColumnDef=\"license\">\n <th mat-header-cell *matHeaderCellDef>{{ 'yuv.shell.settings.dependency-info.license' | translate }}</th>\n <td mat-cell *matCellDef=\"let element\">\n @if (element.link) {\n <a href=\"{{ element.link }}\" target=\"_blank\">{{ element.license }}</a>\n } @else {\n {{ element.license }}\n }\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"['name', 'version', 'license']\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: ['name', 'version', 'license']\"></tr>\n </table>\n </div>\n </ng-template>\n </mat-expansion-panel>\n </section>\n }\n <!-- app settings -->\n <!-- TODO: activate one feature is refined -->\n <!-- @for (c of appSettingForms$ | async; track $index) {\n <section>\n {{ c.label }}\n <form [formGroup]=\"c.form\" (ngSubmit)=\"saveAppSettings(c.appID, c.form)\">\n @for (n of c.formControls; track $index) {\n <label\n >{{ n.label }}\n\n @switch (n.type) {\n @case ('string') {\n <input type=\"text\" [formControlName]=\"n.name\" />\n }\n @case ('number') {\n <input type=\"number\" [formControlName]=\"n.name\" />\n }\n }\n </label>\n }\n <button [ngClass]=\"{ hideen: c.form.untouched }\" [disabled]=\"c.form.invalid\">Save</button>\n </form>\n </section>\n } -->\n </div>\n</main>\n"],"names":[],"mappings":"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"}
|