@sinequa/atomic-angular 1.18.0 → 1.18.2
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/features/user-profile/i18n/de.json +4 -0
- package/features/user-profile/i18n/en.json +4 -0
- package/features/user-profile/i18n/fr.json +4 -0
- package/fesm2022/sinequa-atomic-angular.mjs +57 -10
- package/fesm2022/sinequa-atomic-angular.mjs.map +1 -1
- package/index.d.ts +16 -0
- package/package.json +1 -1
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
"notDefined": "Nicht definiert",
|
|
4
4
|
"deleteProperty": "Eigenschaft löschen",
|
|
5
5
|
"editProperty": "Eigenschaft bearbeiten",
|
|
6
|
+
"saved": "„{{field}}“ gespeichert",
|
|
7
|
+
"deleted": "„{{field}}“ gelöscht",
|
|
8
|
+
"saveFailed": "„{{field}}“ konnte nicht gespeichert werden",
|
|
9
|
+
"deleteFailed": "„{{field}}“ konnte nicht gelöscht werden",
|
|
6
10
|
"data": {
|
|
7
11
|
"mail": "E-Mail",
|
|
8
12
|
"fullName": "Vollständiger Name",
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
"notDefined": "Not defined",
|
|
4
4
|
"deleteProperty": "Delete property",
|
|
5
5
|
"editProperty": "Edit property",
|
|
6
|
+
"saved": "“{{field}}” saved",
|
|
7
|
+
"deleted": "“{{field}}” deleted",
|
|
8
|
+
"saveFailed": "Could not save “{{field}}”",
|
|
9
|
+
"deleteFailed": "Could not delete “{{field}}”",
|
|
6
10
|
"data": {
|
|
7
11
|
"mail": "Mail",
|
|
8
12
|
"fullName": "Full name",
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
"notDefined": "Non défini",
|
|
4
4
|
"deleteProperty": "Supprimer le champ",
|
|
5
5
|
"editProperty": "Modifier le champ",
|
|
6
|
+
"saved": "« {{field}} » enregistré",
|
|
7
|
+
"deleted": "« {{field}} » supprimé",
|
|
8
|
+
"saveFailed": "Impossible d'enregistrer « {{field}} »",
|
|
9
|
+
"deleteFailed": "Impossible de supprimer « {{field}} »",
|
|
6
10
|
"data": {
|
|
7
11
|
"mail": "E-mail",
|
|
8
12
|
"fullName": "Nom complet",
|
|
@@ -5552,11 +5552,11 @@ class SourceComponent {
|
|
|
5552
5552
|
return { iconClass: defaultIconClass };
|
|
5553
5553
|
}, ...(ngDevMode ? [{ debugName: "iconDetails" }] : []));
|
|
5554
5554
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.29", ngImport: i0, type: SourceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5555
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.29", type: SourceComponent, isStandalone: true, selector: "source, Source", inputs: { collection: { classPropertyName: "collection", publicName: "collection", isSignal: true, isRequired: false, transformFunction: null }, connector: { classPropertyName: "connector", publicName: "connector", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideTranslocoScope("sources")], ngImport: i0, template: "@if (iconDetails().iconPath) {\
|
|
5555
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.29", type: SourceComponent, isStandalone: true, selector: "source, Source", inputs: { collection: { classPropertyName: "collection", publicName: "collection", isSignal: true, isRequired: false, transformFunction: null }, connector: { classPropertyName: "connector", publicName: "connector", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideTranslocoScope("sources")], ngImport: i0, template: "@if (iconDetails().iconPath) {\n <img\n class=\"inline-block h-[1em] w-auto max-w-none object-contain align-[-0.125em]\"\n [src]=\"iconDetails().iconPath\"\n [alt]=\"collection()?.[0] || ('sources.sourceIcon' | transloco)\" />\n} @else {\n <FaIcon\n [faClass]=\"iconDetails().iconClass\"\n [attr.aria-label]=\"'sources.sourceIcon' | transloco\" />\n}\n", dependencies: [{ kind: "component", type: FaIconComponent, selector: "fa-icon, FaIcon", inputs: ["faClass", "class"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
5556
5556
|
}
|
|
5557
5557
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.29", ngImport: i0, type: SourceComponent, decorators: [{
|
|
5558
5558
|
type: Component,
|
|
5559
|
-
args: [{ selector: "source, Source", standalone: true, imports: [TranslocoPipe, FaIconComponent], providers: [provideTranslocoScope("sources")], template: "@if (iconDetails().iconPath) {\
|
|
5559
|
+
args: [{ selector: "source, Source", standalone: true, imports: [TranslocoPipe, FaIconComponent], providers: [provideTranslocoScope("sources")], template: "@if (iconDetails().iconPath) {\n <img\n class=\"inline-block h-[1em] w-auto max-w-none object-contain align-[-0.125em]\"\n [src]=\"iconDetails().iconPath\"\n [alt]=\"collection()?.[0] || ('sources.sourceIcon' | transloco)\" />\n} @else {\n <FaIcon\n [faClass]=\"iconDetails().iconClass\"\n [attr.aria-label]=\"'sources.sourceIcon' | transloco\" />\n}\n" }]
|
|
5560
5560
|
}], propDecorators: { collection: [{ type: i0.Input, args: [{ isSignal: true, alias: "collection", required: false }] }], connector: [{ type: i0.Input, args: [{ isSignal: true, alias: "connector", required: false }] }] } });
|
|
5561
5561
|
|
|
5562
5562
|
class DocumentLocatorComponent {
|
|
@@ -18237,6 +18237,10 @@ class UserProfileFormComponent {
|
|
|
18237
18237
|
username = computed(() => this.principalStore.name() || this.principalStore.email() || null, ...(ngDevMode ? [{ debugName: "username" }] : []));
|
|
18238
18238
|
propertyToEdit = signal(undefined, ...(ngDevMode ? [{ debugName: "propertyToEdit" }] : []));
|
|
18239
18239
|
value = model(undefined, ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
18240
|
+
/** `category.propertyName` of the write in flight, to disable the buttons and ignore a second click. */
|
|
18241
|
+
pending = signal(undefined, ...(ngDevMode ? [{ debugName: "pending" }] : []));
|
|
18242
|
+
/** Outcome of the last write, shown in a single message area. */
|
|
18243
|
+
feedback = signal(undefined, ...(ngDevMode ? [{ debugName: "feedback" }] : []));
|
|
18240
18244
|
currentLanguage = model(...(ngDevMode ? [undefined, { debugName: "currentLanguage" }] : []));
|
|
18241
18245
|
AllLanguages = [
|
|
18242
18246
|
{ code: "en", label: "English", icon: FlagEnglishIcon },
|
|
@@ -18244,8 +18248,7 @@ class UserProfileFormComponent {
|
|
|
18244
18248
|
{ code: "de", label: "Deutsch", icon: FlagGermanIcon }
|
|
18245
18249
|
];
|
|
18246
18250
|
changingPassword = signal(false, ...(ngDevMode ? [{ debugName: "changingPassword" }] : []));
|
|
18247
|
-
customData = computed(() => (this.appStore.general()?.features?.userProfile?.customData || [])
|
|
18248
|
-
.filter(key => !this.keys().some(k => k.toLowerCase() === key.toLowerCase())), ...(ngDevMode ? [{ debugName: "customData" }] : []));
|
|
18251
|
+
customData = computed(() => (this.appStore.general()?.features?.userProfile?.customData || []).filter(key => !this.keys().some(k => k.toLowerCase() === key.toLowerCase())), ...(ngDevMode ? [{ debugName: "customData" }] : []));
|
|
18249
18252
|
allowChangePassword = computed(() => {
|
|
18250
18253
|
const { useCredentials } = globalConfig;
|
|
18251
18254
|
const { allowChangePassword = false } = this.appStore.general()?.features || {};
|
|
@@ -18297,9 +18300,34 @@ class UserProfileFormComponent {
|
|
|
18297
18300
|
onEdit(category, propertyName) {
|
|
18298
18301
|
const data = this.userProfile()?.[category];
|
|
18299
18302
|
const value = data?.[propertyName];
|
|
18303
|
+
this.feedback.set(undefined);
|
|
18300
18304
|
this.value.set(value?.trim() || "");
|
|
18301
18305
|
this.propertyToEdit.set(`${category}.${propertyName}`);
|
|
18302
18306
|
}
|
|
18307
|
+
/** Name of the property as the user sees it: the translated label for `data`, the raw key otherwise. */
|
|
18308
|
+
fieldLabel(category, propertyName) {
|
|
18309
|
+
if (category === "data") {
|
|
18310
|
+
const key = `userProfile.data.${propertyName}`;
|
|
18311
|
+
const label = this.transloco.translate(key);
|
|
18312
|
+
if (label !== key)
|
|
18313
|
+
return label;
|
|
18314
|
+
}
|
|
18315
|
+
return propertyName;
|
|
18316
|
+
}
|
|
18317
|
+
/** Builds the outcome message of a write, naming the action and the property. */
|
|
18318
|
+
message(action, category, propertyName) {
|
|
18319
|
+
return this.transloco.translate(`userProfile.${action}`, { field: this.fieldLabel(category, propertyName) });
|
|
18320
|
+
}
|
|
18321
|
+
/** Outcome of the last write when it concerns this `category.propertyName`, to outline the field. */
|
|
18322
|
+
fieldState(key) {
|
|
18323
|
+
const feedback = this.feedback();
|
|
18324
|
+
return feedback?.key === key ? feedback.type : undefined;
|
|
18325
|
+
}
|
|
18326
|
+
/** Leaves the editor without saving. */
|
|
18327
|
+
onCancelEdit() {
|
|
18328
|
+
this.feedback.set(undefined);
|
|
18329
|
+
this.propertyToEdit.set(undefined);
|
|
18330
|
+
}
|
|
18303
18331
|
/**
|
|
18304
18332
|
* On data property deletion, for now we make the property an empty string
|
|
18305
18333
|
*
|
|
@@ -18307,6 +18335,11 @@ class UserProfileFormComponent {
|
|
|
18307
18335
|
* @param propertyName property name
|
|
18308
18336
|
*/
|
|
18309
18337
|
async onDeleteData(category, propertyName) {
|
|
18338
|
+
if (this.pending())
|
|
18339
|
+
return;
|
|
18340
|
+
const key = `${category}.${propertyName}`;
|
|
18341
|
+
this.feedback.set(undefined);
|
|
18342
|
+
this.pending.set(key);
|
|
18310
18343
|
try {
|
|
18311
18344
|
const response = await deleteUserProfileProperty(this.userProfile(), category, propertyName);
|
|
18312
18345
|
if (response) {
|
|
@@ -18314,13 +18347,18 @@ class UserProfileFormComponent {
|
|
|
18314
18347
|
const userProfile = JSON.parse(JSON.stringify(this.userProfile()));
|
|
18315
18348
|
userProfile[category][propertyName] = undefined;
|
|
18316
18349
|
this.userProfileResource.set(userProfile);
|
|
18350
|
+
this.feedback.set({ key, type: "success", message: this.message("deleted", category, propertyName) });
|
|
18317
18351
|
}
|
|
18318
18352
|
else {
|
|
18319
18353
|
info("Could not delete user profile property", response);
|
|
18354
|
+
this.feedback.set({ key, type: "error", message: this.message("deleteFailed", category, propertyName) });
|
|
18320
18355
|
}
|
|
18321
18356
|
}
|
|
18322
|
-
catch
|
|
18323
|
-
|
|
18357
|
+
catch {
|
|
18358
|
+
this.feedback.set({ key, type: "error", message: this.message("deleteFailed", category, propertyName) });
|
|
18359
|
+
}
|
|
18360
|
+
finally {
|
|
18361
|
+
this.pending.set(undefined);
|
|
18324
18362
|
}
|
|
18325
18363
|
}
|
|
18326
18364
|
/**
|
|
@@ -18330,6 +18368,11 @@ class UserProfileFormComponent {
|
|
|
18330
18368
|
* @param propertyName property name
|
|
18331
18369
|
*/
|
|
18332
18370
|
async onSaveData(category, propertyName) {
|
|
18371
|
+
if (this.pending())
|
|
18372
|
+
return;
|
|
18373
|
+
const key = `${category}.${propertyName}`;
|
|
18374
|
+
this.feedback.set(undefined);
|
|
18375
|
+
this.pending.set(key);
|
|
18333
18376
|
// cloning the user profile to avoid changing the signal value before confirming the update
|
|
18334
18377
|
const userProfile = JSON.parse(JSON.stringify(this.userProfile()));
|
|
18335
18378
|
if (!userProfile[category])
|
|
@@ -18348,9 +18391,13 @@ class UserProfileFormComponent {
|
|
|
18348
18391
|
}
|
|
18349
18392
|
this.value.set(undefined);
|
|
18350
18393
|
this.propertyToEdit.set(undefined);
|
|
18394
|
+
this.feedback.set({ key, type: "success", message: this.message("saved", category, propertyName) });
|
|
18351
18395
|
}
|
|
18352
|
-
catch
|
|
18353
|
-
|
|
18396
|
+
catch {
|
|
18397
|
+
this.feedback.set({ key, type: "error", message: this.message("saveFailed", category, propertyName) });
|
|
18398
|
+
}
|
|
18399
|
+
finally {
|
|
18400
|
+
this.pending.set(undefined);
|
|
18354
18401
|
}
|
|
18355
18402
|
}
|
|
18356
18403
|
/**
|
|
@@ -18395,7 +18442,7 @@ class UserProfileFormComponent {
|
|
|
18395
18442
|
}
|
|
18396
18443
|
}
|
|
18397
18444
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.29", ngImport: i0, type: UserProfileFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
18398
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.29", type: UserProfileFormComponent, isStandalone: true, selector: "user-profile-form, UserProfileForm, userprofileform", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, currentLanguage: { classPropertyName: "currentLanguage", publicName: "currentLanguage", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", currentLanguage: "currentLanguageChange" }, host: { classAttribute: "flex flex-col h-full overflow-hidden" }, providers: [provideTranslocoScope("user-profile", "login")], viewQueries: [{ propertyName: "createInputElement", first: true, predicate: ["avatarInput"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (userProfileResource.hasValue()) {\r\n @let profile = userProfile();\r\n <div class=\"flex min-h-0 flex-1 flex-col gap-2 p-4\">\r\n <div class=\"mb-4 flex\">\r\n @if (allowProfilePhoto()) {\r\n <!-- AVATAR -->\r\n <Avatar class=\"size-14 cursor-pointer\" (click)=\"avatarInput.click()\">\r\n @if (profile?.data?.profilePhoto) {\r\n <AvatarImage\r\n [src]=\"profile?.data?.profilePhoto!\"\r\n width=\"44\"\r\n height=\"44\"\r\n alt=\"avatar\" />\r\n }\r\n <AvatarFallback class=\"text-lg\">\r\n @if (initials()) {\r\n <span>{{ initials() }}</span>\r\n } @else {\r\n <UserIcon class=\"size-7\" />\r\n }\r\n </AvatarFallback>\r\n </Avatar>\r\n @if (profile?.data?.profilePhoto) {\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"text-destructive cursor-pointer\"\r\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\r\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\"\r\n (click)=\"onDeleteData('data', 'profilePhoto')\">\r\n <TrashIcon />\r\n </button>\r\n }\r\n }\r\n <div class=\"grow\"></div>\r\n <div class=\"flex flex-col gap-4\">\r\n <!-- LANGUAGE -->\r\n <select\r\n class=\"hover:outline-primary focus:outline-primary border-foreground/10 bg-background hover:bg-muted focus:bg-muted h-8 rounded-md border px-2 hover:outline focus:outline\"\r\n [(ngModel)]=\"currentLanguage\"\r\n (change)=\"changeLanguage()\">\r\n @for (lang of AllLanguages; track lang.code) {\r\n <option [value]=\"lang.code\">\r\n {{ lang.label }}\r\n </option>\r\n }\r\n </select>\r\n <!-- CHANGE PASSWORD -->\r\n @if (allowChangePassword()) {\r\n <button\r\n [disabled]=\"changingPassword()\"\r\n (click)=\"changingPassword.set(true)\">\r\n {{ \"login.changePassword\" | transloco }}\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n @if (changingPassword()) {\r\n <ChangePassword\r\n [username]=\"username()\"\r\n [redirectAfterSuccess]=\"false\"\r\n [redirectAfterCancel]=\"false\"\r\n (cancel)=\"changingPassword.set(false)\"\r\n (success)=\"changingPassword.set(false)\" />\r\n } @else {\r\n <div class=\"min-h-0 flex-1 overflow-auto\">\r\n <div>\r\n @for (key of formKeys(); track key) {\r\n <div>\r\n <p>{{ `userProfile.data.${key}` | transloco }}</p>\r\n <div class=\"group flex flex-row\">\r\n <div\r\n class=\"min-w-0 grow break-words whitespace-pre-line\"\r\n [class.text-muted-foreground]=\"!getDataValue('data', key)\">\r\n @if (propertyToEdit() === \"data.\" + key) {\r\n <textarea\r\n class=\"hover:outline-primary focus:outline-primary border-foreground/20 hover:bg-muted focus:bg-muted mt-2 w-full rounded-md border px-2 pt-1 hover:outline focus:outline\"\r\n id=\"user-profile-{{ key }}\"\r\n [(ngModel)]=\"value\"></textarea>\r\n } @else {\r\n {{\r\n getDataValue(\"data\", key) ||\r\n (\"userProfile.notDefined\" | transloco)\r\n }}\r\n }\r\n </div>\r\n @if (propertyToEdit() === \"data.\" + key) {\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"visible mx-2 cursor-pointer sm:invisible sm:group-hover:visible\"\r\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\r\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\r\n (click)=\"onSaveData('data', key)\">\r\n <save-icon class=\"ml-2\" />\r\n </button>\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"visible cursor-pointer sm:invisible sm:group-hover:visible\"\r\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\r\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\r\n (click)=\"propertyToEdit.set(undefined)\">\r\n <UndoIcon />\r\n </button>\r\n } @else {\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"visible mx-2 cursor-pointer sm:invisible sm:group-hover:visible\"\r\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\r\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\r\n (click)=\"onEdit('data', key)\">\r\n <EditIcon />\r\n </button>\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"text-destructive visible cursor-pointer sm:invisible sm:group-hover:visible\"\r\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\r\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\"\r\n (click)=\"onDeleteData('data', key)\">\r\n <TrashIcon />\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n @for (key of customData(); track key) {\r\n <div>\r\n <p>{{ key }}</p>\r\n <div class=\"group flex flex-row\">\r\n <div\r\n class=\"min-w-0 grow break-words whitespace-pre-line\"\r\n [class.text-muted-foreground]=\"\r\n !getDataValue('customData', key)\r\n \">\r\n @if (propertyToEdit() === \"customData.\" + key) {\r\n <textarea\r\n class=\"hover:outline-primary focus:outline-primary border-foreground/20 hover:bg-muted focus:bg-muted mt-2 w-full rounded-md border px-2 pt-1 hover:outline focus:outline\"\r\n id=\"user-profile-{{ key }}\"\r\n [(ngModel)]=\"value\"></textarea>\r\n } @else {\r\n {{\r\n getDataValue(\"customData\", key) ||\r\n (\"userProfile.notDefined\" | transloco)\r\n }}\r\n }\r\n </div>\r\n @if (propertyToEdit() === \"customData.\" + key) {\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"visible mx-2 cursor-pointer sm:invisible sm:group-hover:visible\"\r\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\r\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\r\n (click)=\"onSaveData('customData', key)\">\r\n <save-icon class=\"ml-2\" />\r\n </button>\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"visible cursor-pointer sm:invisible sm:group-hover:visible\"\r\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\r\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\r\n (click)=\"propertyToEdit.set(undefined)\">\r\n <UndoIcon />\r\n </button>\r\n } @else {\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"visible mx-2 cursor-pointer sm:invisible sm:group-hover:visible\"\r\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\r\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\r\n (click)=\"onEdit('customData', key)\">\r\n <EditIcon />\r\n </button>\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"text-destructive visible cursor-pointer sm:invisible sm:group-hover:visible\"\r\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\r\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\"\r\n (click)=\"onDeleteData('customData', key)\">\r\n <TrashIcon />\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n} @else if (userProfileResource.isLoading()) {\r\n <span>Loading...</span>\r\n} @else if (userProfileResource.error()) {\r\n <p>Please contact an administrator to create your user profile.</p>\r\n}\r\n\r\n<!-- AVATAR UPLOAD -->\r\n<input\r\n #avatarInput\r\n class=\"hidden\"\r\n type=\"file\"\r\n accept=\"image/*\"\r\n (change)=\"uploadAvatar($event)\" />\r\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { 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.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: RouterModule }, { kind: "component", type: TrashIcon, selector: "trash-icon, TrashIcon", inputs: ["class"] }, { kind: "component", type: EditIcon, selector: "edit-icon, EditIcon, editicon", inputs: ["class"] }, { kind: "component", type: UndoIcon, selector: "UndoIcon, undoicon, undo-icon", inputs: ["class"] }, { kind: "component", type: SaveIcon, selector: "save-icon, SaveIcon", inputs: ["class"] }, { kind: "component", type: AvatarComponent, selector: "avatar, Avatar", inputs: ["class", "size"] }, { kind: "component", type: AvatarFallbackComponent, selector: "avatar-fallback, avatarfallback, AvatarFallback", inputs: ["class", "size"] }, { kind: "component", type: UserIcon, selector: "user-icon, UserIcon", inputs: ["class"] }, { kind: "component", type: AvatarImageComponent, selector: "avatar-image, AvatarImage, avatarimage", inputs: ["class", "src", "alt", "width", "height", "referrerPolicy", "crossOrigin"] }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size", "solid"] }, { kind: "component", type: ChangePasswordComponent, selector: "change-password, ChangePassword, changepassword", inputs: ["username", "alert", "redirectAfterSuccess", "redirectAfterCancel", "currentPassword", "newPassword", "confirmPassword"], outputs: ["success", "cancel", "currentPasswordChange", "newPasswordChange", "confirmPasswordChange"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
18445
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.29", type: UserProfileFormComponent, isStandalone: true, selector: "user-profile-form, UserProfileForm, userprofileform", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, currentLanguage: { classPropertyName: "currentLanguage", publicName: "currentLanguage", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", currentLanguage: "currentLanguageChange" }, host: { classAttribute: "flex flex-col h-full overflow-hidden" }, providers: [provideTranslocoScope("user-profile", "login")], viewQueries: [{ propertyName: "createInputElement", first: true, predicate: ["avatarInput"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (userProfileResource.hasValue()) {\n @let profile = userProfile();\n <div class=\"flex min-h-0 flex-1 flex-col gap-2 p-4\">\n <div class=\"mb-4 flex\">\n @if (allowProfilePhoto()) {\n <!-- AVATAR -->\n <Avatar\n class=\"size-14 cursor-pointer\"\n [class.ring-2]=\"!!fieldState('data.profilePhoto')\"\n [class.ring-destructive]=\"fieldState('data.profilePhoto') === 'error'\"\n [class.ring-green-600]=\"fieldState('data.profilePhoto') === 'success'\"\n (click)=\"avatarInput.click()\">\n @if (profile?.data?.profilePhoto) {\n <AvatarImage\n [src]=\"profile?.data?.profilePhoto!\"\n width=\"44\"\n height=\"44\"\n alt=\"avatar\" />\n }\n <AvatarFallback class=\"text-lg\">\n @if (initials()) {\n <span>{{ initials() }}</span>\n } @else {\n <UserIcon class=\"size-7\" />\n }\n </AvatarFallback>\n </Avatar>\n @if (profile?.data?.profilePhoto) {\n <button\n variant=\"ghost\"\n [iconOnly]=\"true\"\n class=\"text-destructive cursor-pointer\"\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\"\n [disabled]=\"!!pending()\"\n (click)=\"onDeleteData('data', 'profilePhoto')\">\n <TrashIcon />\n </button>\n }\n }\n <div class=\"grow\"></div>\n <div class=\"flex flex-col gap-4\">\n <!-- LANGUAGE -->\n <select\n class=\"hover:outline-primary focus:outline-primary border-foreground/10 bg-background hover:bg-muted focus:bg-muted h-8 rounded-md border px-2 hover:outline focus:outline\"\n [(ngModel)]=\"currentLanguage\"\n (change)=\"changeLanguage()\">\n @for (lang of AllLanguages; track lang.code) {\n <option [value]=\"lang.code\">\n {{ lang.label }}\n </option>\n }\n </select>\n <!-- CHANGE PASSWORD -->\n @if (allowChangePassword()) {\n <button\n [disabled]=\"changingPassword()\"\n (click)=\"changingPassword.set(true)\">\n {{ \"login.changePassword\" | transloco }}\n </button>\n }\n </div>\n </div>\n <!-- always rendered, so the room is reserved and the layout doesn't shift when a message appears -->\n <p\n class=\"min-h-5 text-sm\"\n role=\"status\"\n [attr.aria-live]=\"feedback()?.type === 'error' ? 'assertive' : 'polite'\"\n [class.text-destructive]=\"feedback()?.type === 'error'\"\n [class.text-green-600]=\"feedback()?.type === 'success'\">\n {{ feedback()?.message }}\n </p>\n @if (changingPassword()) {\n <ChangePassword\n [username]=\"username()\"\n [redirectAfterSuccess]=\"false\"\n [redirectAfterCancel]=\"false\"\n (cancel)=\"changingPassword.set(false)\"\n (success)=\"changingPassword.set(false)\" />\n } @else {\n <!-- -m-1 p-1: room for the outline of a field, which the scroll container would otherwise clip -->\n <div class=\"-m-1 min-h-0 flex-1 overflow-auto p-1\">\n <div>\n @for (key of formKeys(); track key) {\n <div>\n <p class=\"px-2\">{{ `userProfile.data.${key}` | transloco }}</p>\n <div\n class=\"group flex flex-row rounded-md px-2 py-0.5\"\n [class.ring-1]=\"!!fieldState('data.' + key)\"\n [class.ring-destructive]=\"fieldState('data.' + key) === 'error'\"\n [class.ring-green-600]=\"\n fieldState('data.' + key) === 'success'\n \">\n <div\n class=\"min-w-0 grow break-words whitespace-pre-line\"\n [class.text-muted-foreground]=\"!getDataValue('data', key)\">\n @if (propertyToEdit() === \"data.\" + key) {\n <textarea\n class=\"hover:outline-primary focus:outline-primary border-foreground/20 hover:bg-muted focus:bg-muted mt-2 w-full rounded-md border px-2 pt-1 hover:outline focus:outline\"\n id=\"user-profile-{{ key }}\"\n [(ngModel)]=\"value\"></textarea>\n } @else {\n {{\n getDataValue(\"data\", key) ||\n (\"userProfile.notDefined\" | transloco)\n }}\n }\n </div>\n @if (propertyToEdit() === \"data.\" + key) {\n <button\n variant=\"ghost\"\n [iconOnly]=\"true\"\n class=\"visible mx-2 cursor-pointer sm:invisible sm:group-hover:visible\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n [disabled]=\"!!pending()\"\n (click)=\"onSaveData('data', key)\">\n <save-icon class=\"ml-2\" />\n </button>\n <button\n variant=\"ghost\"\n [iconOnly]=\"true\"\n class=\"visible cursor-pointer sm:invisible sm:group-hover:visible\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onCancelEdit()\">\n <UndoIcon />\n </button>\n } @else {\n <button\n variant=\"ghost\"\n [iconOnly]=\"true\"\n class=\"visible mx-2 cursor-pointer sm:invisible sm:group-hover:visible\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onEdit('data', key)\">\n <EditIcon />\n </button>\n <button\n variant=\"ghost\"\n [iconOnly]=\"true\"\n class=\"text-destructive visible cursor-pointer sm:invisible sm:group-hover:visible\"\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\"\n [disabled]=\"!!pending()\"\n (click)=\"onDeleteData('data', key)\">\n <TrashIcon />\n </button>\n }\n </div>\n </div>\n }\n @for (key of customData(); track key) {\n <div>\n <p class=\"px-2\">{{ key }}</p>\n <div\n class=\"group flex flex-row rounded-md px-2 py-0.5\"\n [class.ring-1]=\"!!fieldState('customData.' + key)\"\n [class.ring-destructive]=\"\n fieldState('customData.' + key) === 'error'\n \"\n [class.ring-green-600]=\"\n fieldState('customData.' + key) === 'success'\n \">\n <div\n class=\"min-w-0 grow break-words whitespace-pre-line\"\n [class.text-muted-foreground]=\"\n !getDataValue('customData', key)\n \">\n @if (propertyToEdit() === \"customData.\" + key) {\n <textarea\n class=\"hover:outline-primary focus:outline-primary border-foreground/20 hover:bg-muted focus:bg-muted mt-2 w-full rounded-md border px-2 pt-1 hover:outline focus:outline\"\n id=\"user-profile-{{ key }}\"\n [(ngModel)]=\"value\"></textarea>\n } @else {\n {{\n getDataValue(\"customData\", key) ||\n (\"userProfile.notDefined\" | transloco)\n }}\n }\n </div>\n @if (propertyToEdit() === \"customData.\" + key) {\n <button\n variant=\"ghost\"\n [iconOnly]=\"true\"\n class=\"visible mx-2 cursor-pointer sm:invisible sm:group-hover:visible\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n [disabled]=\"!!pending()\"\n (click)=\"onSaveData('customData', key)\">\n <save-icon class=\"ml-2\" />\n </button>\n <button\n variant=\"ghost\"\n [iconOnly]=\"true\"\n class=\"visible cursor-pointer sm:invisible sm:group-hover:visible\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onCancelEdit()\">\n <UndoIcon />\n </button>\n } @else {\n <button\n variant=\"ghost\"\n [iconOnly]=\"true\"\n class=\"visible mx-2 cursor-pointer sm:invisible sm:group-hover:visible\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onEdit('customData', key)\">\n <EditIcon />\n </button>\n <button\n variant=\"ghost\"\n [iconOnly]=\"true\"\n class=\"text-destructive visible cursor-pointer sm:invisible sm:group-hover:visible\"\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\"\n [disabled]=\"!!pending()\"\n (click)=\"onDeleteData('customData', key)\">\n <TrashIcon />\n </button>\n }\n </div>\n </div>\n }\n </div>\n </div>\n }\n </div>\n} @else if (userProfileResource.isLoading()) {\n <span>Loading...</span>\n} @else if (userProfileResource.error()) {\n <p>Please contact an administrator to create your user profile.</p>\n}\n\n<!-- AVATAR UPLOAD -->\n<input\n #avatarInput\n class=\"hidden\"\n type=\"file\"\n accept=\"image/*\"\n (change)=\"uploadAvatar($event)\" />\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { 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.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: RouterModule }, { kind: "component", type: TrashIcon, selector: "trash-icon, TrashIcon", inputs: ["class"] }, { kind: "component", type: EditIcon, selector: "edit-icon, EditIcon, editicon", inputs: ["class"] }, { kind: "component", type: UndoIcon, selector: "UndoIcon, undoicon, undo-icon", inputs: ["class"] }, { kind: "component", type: SaveIcon, selector: "save-icon, SaveIcon", inputs: ["class"] }, { kind: "component", type: AvatarComponent, selector: "avatar, Avatar", inputs: ["class", "size"] }, { kind: "component", type: AvatarFallbackComponent, selector: "avatar-fallback, avatarfallback, AvatarFallback", inputs: ["class", "size"] }, { kind: "component", type: UserIcon, selector: "user-icon, UserIcon", inputs: ["class"] }, { kind: "component", type: AvatarImageComponent, selector: "avatar-image, AvatarImage, avatarimage", inputs: ["class", "src", "alt", "width", "height", "referrerPolicy", "crossOrigin"] }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size", "solid"] }, { kind: "component", type: ChangePasswordComponent, selector: "change-password, ChangePassword, changepassword", inputs: ["username", "alert", "redirectAfterSuccess", "redirectAfterCancel", "currentPassword", "newPassword", "confirmPassword"], outputs: ["success", "cancel", "currentPasswordChange", "newPasswordChange", "confirmPasswordChange"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
18399
18446
|
}
|
|
18400
18447
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.29", ngImport: i0, type: UserProfileFormComponent, decorators: [{
|
|
18401
18448
|
type: Component,
|
|
@@ -18413,7 +18460,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.29", ngImpo
|
|
|
18413
18460
|
AvatarImageComponent,
|
|
18414
18461
|
ButtonComponent,
|
|
18415
18462
|
ChangePasswordComponent
|
|
18416
|
-
], providers: [provideTranslocoScope("user-profile", "login")], template: "@if (userProfileResource.hasValue()) {\r\n @let profile = userProfile();\r\n <div class=\"flex min-h-0 flex-1 flex-col gap-2 p-4\">\r\n <div class=\"mb-4 flex\">\r\n @if (allowProfilePhoto()) {\r\n <!-- AVATAR -->\r\n <Avatar class=\"size-14 cursor-pointer\" (click)=\"avatarInput.click()\">\r\n @if (profile?.data?.profilePhoto) {\r\n <AvatarImage\r\n [src]=\"profile?.data?.profilePhoto!\"\r\n width=\"44\"\r\n height=\"44\"\r\n alt=\"avatar\" />\r\n }\r\n <AvatarFallback class=\"text-lg\">\r\n @if (initials()) {\r\n <span>{{ initials() }}</span>\r\n } @else {\r\n <UserIcon class=\"size-7\" />\r\n }\r\n </AvatarFallback>\r\n </Avatar>\r\n @if (profile?.data?.profilePhoto) {\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"text-destructive cursor-pointer\"\r\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\r\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\"\r\n (click)=\"onDeleteData('data', 'profilePhoto')\">\r\n <TrashIcon />\r\n </button>\r\n }\r\n }\r\n <div class=\"grow\"></div>\r\n <div class=\"flex flex-col gap-4\">\r\n <!-- LANGUAGE -->\r\n <select\r\n class=\"hover:outline-primary focus:outline-primary border-foreground/10 bg-background hover:bg-muted focus:bg-muted h-8 rounded-md border px-2 hover:outline focus:outline\"\r\n [(ngModel)]=\"currentLanguage\"\r\n (change)=\"changeLanguage()\">\r\n @for (lang of AllLanguages; track lang.code) {\r\n <option [value]=\"lang.code\">\r\n {{ lang.label }}\r\n </option>\r\n }\r\n </select>\r\n <!-- CHANGE PASSWORD -->\r\n @if (allowChangePassword()) {\r\n <button\r\n [disabled]=\"changingPassword()\"\r\n (click)=\"changingPassword.set(true)\">\r\n {{ \"login.changePassword\" | transloco }}\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n @if (changingPassword()) {\r\n <ChangePassword\r\n [username]=\"username()\"\r\n [redirectAfterSuccess]=\"false\"\r\n [redirectAfterCancel]=\"false\"\r\n (cancel)=\"changingPassword.set(false)\"\r\n (success)=\"changingPassword.set(false)\" />\r\n } @else {\r\n <div class=\"min-h-0 flex-1 overflow-auto\">\r\n <div>\r\n @for (key of formKeys(); track key) {\r\n <div>\r\n <p>{{ `userProfile.data.${key}` | transloco }}</p>\r\n <div class=\"group flex flex-row\">\r\n <div\r\n class=\"min-w-0 grow break-words whitespace-pre-line\"\r\n [class.text-muted-foreground]=\"!getDataValue('data', key)\">\r\n @if (propertyToEdit() === \"data.\" + key) {\r\n <textarea\r\n class=\"hover:outline-primary focus:outline-primary border-foreground/20 hover:bg-muted focus:bg-muted mt-2 w-full rounded-md border px-2 pt-1 hover:outline focus:outline\"\r\n id=\"user-profile-{{ key }}\"\r\n [(ngModel)]=\"value\"></textarea>\r\n } @else {\r\n {{\r\n getDataValue(\"data\", key) ||\r\n (\"userProfile.notDefined\" | transloco)\r\n }}\r\n }\r\n </div>\r\n @if (propertyToEdit() === \"data.\" + key) {\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"visible mx-2 cursor-pointer sm:invisible sm:group-hover:visible\"\r\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\r\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\r\n (click)=\"onSaveData('data', key)\">\r\n <save-icon class=\"ml-2\" />\r\n </button>\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"visible cursor-pointer sm:invisible sm:group-hover:visible\"\r\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\r\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\r\n (click)=\"propertyToEdit.set(undefined)\">\r\n <UndoIcon />\r\n </button>\r\n } @else {\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"visible mx-2 cursor-pointer sm:invisible sm:group-hover:visible\"\r\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\r\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\r\n (click)=\"onEdit('data', key)\">\r\n <EditIcon />\r\n </button>\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"text-destructive visible cursor-pointer sm:invisible sm:group-hover:visible\"\r\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\r\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\"\r\n (click)=\"onDeleteData('data', key)\">\r\n <TrashIcon />\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n @for (key of customData(); track key) {\r\n <div>\r\n <p>{{ key }}</p>\r\n <div class=\"group flex flex-row\">\r\n <div\r\n class=\"min-w-0 grow break-words whitespace-pre-line\"\r\n [class.text-muted-foreground]=\"\r\n !getDataValue('customData', key)\r\n \">\r\n @if (propertyToEdit() === \"customData.\" + key) {\r\n <textarea\r\n class=\"hover:outline-primary focus:outline-primary border-foreground/20 hover:bg-muted focus:bg-muted mt-2 w-full rounded-md border px-2 pt-1 hover:outline focus:outline\"\r\n id=\"user-profile-{{ key }}\"\r\n [(ngModel)]=\"value\"></textarea>\r\n } @else {\r\n {{\r\n getDataValue(\"customData\", key) ||\r\n (\"userProfile.notDefined\" | transloco)\r\n }}\r\n }\r\n </div>\r\n @if (propertyToEdit() === \"customData.\" + key) {\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"visible mx-2 cursor-pointer sm:invisible sm:group-hover:visible\"\r\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\r\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\r\n (click)=\"onSaveData('customData', key)\">\r\n <save-icon class=\"ml-2\" />\r\n </button>\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"visible cursor-pointer sm:invisible sm:group-hover:visible\"\r\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\r\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\r\n (click)=\"propertyToEdit.set(undefined)\">\r\n <UndoIcon />\r\n </button>\r\n } @else {\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"visible mx-2 cursor-pointer sm:invisible sm:group-hover:visible\"\r\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\r\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\r\n (click)=\"onEdit('customData', key)\">\r\n <EditIcon />\r\n </button>\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"text-destructive visible cursor-pointer sm:invisible sm:group-hover:visible\"\r\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\r\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\"\r\n (click)=\"onDeleteData('customData', key)\">\r\n <TrashIcon />\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n} @else if (userProfileResource.isLoading()) {\r\n <span>Loading...</span>\r\n} @else if (userProfileResource.error()) {\r\n <p>Please contact an administrator to create your user profile.</p>\r\n}\r\n\r\n<!-- AVATAR UPLOAD -->\r\n<input\r\n #avatarInput\r\n class=\"hidden\"\r\n type=\"file\"\r\n accept=\"image/*\"\r\n (change)=\"uploadAvatar($event)\" />\r\n" }]
|
|
18463
|
+
], providers: [provideTranslocoScope("user-profile", "login")], template: "@if (userProfileResource.hasValue()) {\n @let profile = userProfile();\n <div class=\"flex min-h-0 flex-1 flex-col gap-2 p-4\">\n <div class=\"mb-4 flex\">\n @if (allowProfilePhoto()) {\n <!-- AVATAR -->\n <Avatar\n class=\"size-14 cursor-pointer\"\n [class.ring-2]=\"!!fieldState('data.profilePhoto')\"\n [class.ring-destructive]=\"fieldState('data.profilePhoto') === 'error'\"\n [class.ring-green-600]=\"fieldState('data.profilePhoto') === 'success'\"\n (click)=\"avatarInput.click()\">\n @if (profile?.data?.profilePhoto) {\n <AvatarImage\n [src]=\"profile?.data?.profilePhoto!\"\n width=\"44\"\n height=\"44\"\n alt=\"avatar\" />\n }\n <AvatarFallback class=\"text-lg\">\n @if (initials()) {\n <span>{{ initials() }}</span>\n } @else {\n <UserIcon class=\"size-7\" />\n }\n </AvatarFallback>\n </Avatar>\n @if (profile?.data?.profilePhoto) {\n <button\n variant=\"ghost\"\n [iconOnly]=\"true\"\n class=\"text-destructive cursor-pointer\"\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\"\n [disabled]=\"!!pending()\"\n (click)=\"onDeleteData('data', 'profilePhoto')\">\n <TrashIcon />\n </button>\n }\n }\n <div class=\"grow\"></div>\n <div class=\"flex flex-col gap-4\">\n <!-- LANGUAGE -->\n <select\n class=\"hover:outline-primary focus:outline-primary border-foreground/10 bg-background hover:bg-muted focus:bg-muted h-8 rounded-md border px-2 hover:outline focus:outline\"\n [(ngModel)]=\"currentLanguage\"\n (change)=\"changeLanguage()\">\n @for (lang of AllLanguages; track lang.code) {\n <option [value]=\"lang.code\">\n {{ lang.label }}\n </option>\n }\n </select>\n <!-- CHANGE PASSWORD -->\n @if (allowChangePassword()) {\n <button\n [disabled]=\"changingPassword()\"\n (click)=\"changingPassword.set(true)\">\n {{ \"login.changePassword\" | transloco }}\n </button>\n }\n </div>\n </div>\n <!-- always rendered, so the room is reserved and the layout doesn't shift when a message appears -->\n <p\n class=\"min-h-5 text-sm\"\n role=\"status\"\n [attr.aria-live]=\"feedback()?.type === 'error' ? 'assertive' : 'polite'\"\n [class.text-destructive]=\"feedback()?.type === 'error'\"\n [class.text-green-600]=\"feedback()?.type === 'success'\">\n {{ feedback()?.message }}\n </p>\n @if (changingPassword()) {\n <ChangePassword\n [username]=\"username()\"\n [redirectAfterSuccess]=\"false\"\n [redirectAfterCancel]=\"false\"\n (cancel)=\"changingPassword.set(false)\"\n (success)=\"changingPassword.set(false)\" />\n } @else {\n <!-- -m-1 p-1: room for the outline of a field, which the scroll container would otherwise clip -->\n <div class=\"-m-1 min-h-0 flex-1 overflow-auto p-1\">\n <div>\n @for (key of formKeys(); track key) {\n <div>\n <p class=\"px-2\">{{ `userProfile.data.${key}` | transloco }}</p>\n <div\n class=\"group flex flex-row rounded-md px-2 py-0.5\"\n [class.ring-1]=\"!!fieldState('data.' + key)\"\n [class.ring-destructive]=\"fieldState('data.' + key) === 'error'\"\n [class.ring-green-600]=\"\n fieldState('data.' + key) === 'success'\n \">\n <div\n class=\"min-w-0 grow break-words whitespace-pre-line\"\n [class.text-muted-foreground]=\"!getDataValue('data', key)\">\n @if (propertyToEdit() === \"data.\" + key) {\n <textarea\n class=\"hover:outline-primary focus:outline-primary border-foreground/20 hover:bg-muted focus:bg-muted mt-2 w-full rounded-md border px-2 pt-1 hover:outline focus:outline\"\n id=\"user-profile-{{ key }}\"\n [(ngModel)]=\"value\"></textarea>\n } @else {\n {{\n getDataValue(\"data\", key) ||\n (\"userProfile.notDefined\" | transloco)\n }}\n }\n </div>\n @if (propertyToEdit() === \"data.\" + key) {\n <button\n variant=\"ghost\"\n [iconOnly]=\"true\"\n class=\"visible mx-2 cursor-pointer sm:invisible sm:group-hover:visible\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n [disabled]=\"!!pending()\"\n (click)=\"onSaveData('data', key)\">\n <save-icon class=\"ml-2\" />\n </button>\n <button\n variant=\"ghost\"\n [iconOnly]=\"true\"\n class=\"visible cursor-pointer sm:invisible sm:group-hover:visible\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onCancelEdit()\">\n <UndoIcon />\n </button>\n } @else {\n <button\n variant=\"ghost\"\n [iconOnly]=\"true\"\n class=\"visible mx-2 cursor-pointer sm:invisible sm:group-hover:visible\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onEdit('data', key)\">\n <EditIcon />\n </button>\n <button\n variant=\"ghost\"\n [iconOnly]=\"true\"\n class=\"text-destructive visible cursor-pointer sm:invisible sm:group-hover:visible\"\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\"\n [disabled]=\"!!pending()\"\n (click)=\"onDeleteData('data', key)\">\n <TrashIcon />\n </button>\n }\n </div>\n </div>\n }\n @for (key of customData(); track key) {\n <div>\n <p class=\"px-2\">{{ key }}</p>\n <div\n class=\"group flex flex-row rounded-md px-2 py-0.5\"\n [class.ring-1]=\"!!fieldState('customData.' + key)\"\n [class.ring-destructive]=\"\n fieldState('customData.' + key) === 'error'\n \"\n [class.ring-green-600]=\"\n fieldState('customData.' + key) === 'success'\n \">\n <div\n class=\"min-w-0 grow break-words whitespace-pre-line\"\n [class.text-muted-foreground]=\"\n !getDataValue('customData', key)\n \">\n @if (propertyToEdit() === \"customData.\" + key) {\n <textarea\n class=\"hover:outline-primary focus:outline-primary border-foreground/20 hover:bg-muted focus:bg-muted mt-2 w-full rounded-md border px-2 pt-1 hover:outline focus:outline\"\n id=\"user-profile-{{ key }}\"\n [(ngModel)]=\"value\"></textarea>\n } @else {\n {{\n getDataValue(\"customData\", key) ||\n (\"userProfile.notDefined\" | transloco)\n }}\n }\n </div>\n @if (propertyToEdit() === \"customData.\" + key) {\n <button\n variant=\"ghost\"\n [iconOnly]=\"true\"\n class=\"visible mx-2 cursor-pointer sm:invisible sm:group-hover:visible\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n [disabled]=\"!!pending()\"\n (click)=\"onSaveData('customData', key)\">\n <save-icon class=\"ml-2\" />\n </button>\n <button\n variant=\"ghost\"\n [iconOnly]=\"true\"\n class=\"visible cursor-pointer sm:invisible sm:group-hover:visible\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onCancelEdit()\">\n <UndoIcon />\n </button>\n } @else {\n <button\n variant=\"ghost\"\n [iconOnly]=\"true\"\n class=\"visible mx-2 cursor-pointer sm:invisible sm:group-hover:visible\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onEdit('customData', key)\">\n <EditIcon />\n </button>\n <button\n variant=\"ghost\"\n [iconOnly]=\"true\"\n class=\"text-destructive visible cursor-pointer sm:invisible sm:group-hover:visible\"\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\"\n [disabled]=\"!!pending()\"\n (click)=\"onDeleteData('customData', key)\">\n <TrashIcon />\n </button>\n }\n </div>\n </div>\n }\n </div>\n </div>\n }\n </div>\n} @else if (userProfileResource.isLoading()) {\n <span>Loading...</span>\n} @else if (userProfileResource.error()) {\n <p>Please contact an administrator to create your user profile.</p>\n}\n\n<!-- AVATAR UPLOAD -->\n<input\n #avatarInput\n class=\"hidden\"\n type=\"file\"\n accept=\"image/*\"\n (change)=\"uploadAvatar($event)\" />\n" }]
|
|
18417
18464
|
}], ctorParameters: () => [], propDecorators: { createInputElement: [{ type: i0.ViewChild, args: ["avatarInput", { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], currentLanguage: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentLanguage", required: false }] }, { type: i0.Output, args: ["currentLanguageChange"] }] } });
|
|
18418
18465
|
|
|
18419
18466
|
class UserProfileDialog {
|