From a0e6f16323ad57caac8ae7b2a1d4d3873aaf6a97 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 11 Oct 2023 11:49:32 +0200 Subject: [PATCH] split user profile settings + user access settings into its own components --- .../user-group-workspace-editor.element.ts | 5 - .../user-workspace-access-settings.element.ts | 136 ++++++++++++ ...user-workspace-profile-settings.element.ts | 138 ++++++++++++ .../user-workspace-editor.element.ts | 201 ++---------------- 4 files changed, 288 insertions(+), 192 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/components/user-workspace-access-settings/user-workspace-access-settings.element.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/components/user-workspace-profile-settings/user-workspace-profile-settings.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace-editor.element.ts index d97be4ee23..2f52cdae35 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace-editor.element.ts @@ -186,11 +186,6 @@ export class UmbUserGroupWorkspaceEditorElement extends UmbLitElement { flex-direction: column; gap: var(--uui-size-space-2); } - hr { - border: none; - border-bottom: 1px solid var(--uui-color-divider); - width: 100%; - } uui-input { width: 100%; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/components/user-workspace-access-settings/user-workspace-access-settings.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/components/user-workspace-access-settings/user-workspace-access-settings.element.ts new file mode 100644 index 0000000000..5cd9c287f8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/components/user-workspace-access-settings/user-workspace-access-settings.element.ts @@ -0,0 +1,136 @@ +import { UMB_USER_WORKSPACE_CONTEXT } from '../../user-workspace.context.js'; +import { html, customElement, state, css, repeat } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { UserResponseModel } from '@umbraco-cms/backoffice/backend-api'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; +import { UmbInputDocumentElement } from '@umbraco-cms/backoffice/document'; +import { UmbInputMediaElement } from '@umbraco-cms/backoffice/media'; +import { UmbUserGroupInputElement } from '@umbraco-cms/backoffice/user-group'; + +@customElement('umb-user-workspace-access-settings') +export class UmbUserWorkspaceAccessSettingsElement extends UmbLitElement { + @state() + private _user?: UserResponseModel; + + #userWorkspaceContext?: typeof UMB_USER_WORKSPACE_CONTEXT.TYPE; + + constructor() { + super(); + + this.consumeContext(UMB_USER_WORKSPACE_CONTEXT, (instance) => { + this.#userWorkspaceContext = instance; + this.observe(this.#userWorkspaceContext.data, (user) => (this._user = user)); + }); + } + + #onUserGroupsChange(event: UmbChangeEvent) { + const target = event.target as UmbUserGroupInputElement; + this.#userWorkspaceContext?.updateProperty('userGroupIds', target.selectedIds); + } + + #onDocumentStartNodeChange(event: UmbChangeEvent) { + const target = event.target as UmbInputDocumentElement; + this.#userWorkspaceContext?.updateProperty('contentStartNodeIds', target.selectedIds); + } + + #onMediaStartNodeChange(event: UmbChangeEvent) { + const target = event.target as UmbInputMediaElement; + this.#userWorkspaceContext?.updateProperty('mediaStartNodeIds', target.selectedIds); + } + + render() { + return html` +
Assign Access
+
+ + + + + + + + + +
+
+ + +
+ Based on the assigned groups and start nodes, the user has access to the following nodes +
+ + Content + ${this.#renderDocumentStartNodes()} +
+ Media + + + +
`; + } + + #renderDocumentStartNodes() { + if (!this._user || !this._user.contentStartNodeIds) return; + + if (this._user.contentStartNodeIds.length < 1) + return html` + + + + `; + + //TODO Render the name of the content start node instead of it's id. + return repeat( + this._user.contentStartNodeIds, + (node) => node, + (node) => { + return html` + + + + `; + }, + ); + } + + static styles = [ + UmbTextStyles, + css` + hr { + border: none; + border-bottom: 1px solid var(--uui-color-divider); + width: 100%; + } + .faded-text { + color: var(--uui-color-text-alt); + font-size: 0.8rem; + } + `, + ]; +} + +export default UmbUserWorkspaceAccessSettingsElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-user-workspace-access-settings': UmbUserWorkspaceAccessSettingsElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/components/user-workspace-profile-settings/user-workspace-profile-settings.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/components/user-workspace-profile-settings/user-workspace-profile-settings.element.ts new file mode 100644 index 0000000000..de8c853f1d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/components/user-workspace-profile-settings/user-workspace-profile-settings.element.ts @@ -0,0 +1,138 @@ +import { UMB_USER_WORKSPACE_CONTEXT } from '../../user-workspace.context.js'; +import { html, customElement, state, ifDefined, css } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { UUISelectElement } from '@umbraco-cms/backoffice/external/uui'; +import { UserResponseModel } from '@umbraco-cms/backoffice/backend-api'; +import { UMB_AUTH, UmbLoggedInUser } from '@umbraco-cms/backoffice/auth'; +import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; + +@customElement('umb-user-workspace-profile-settings') +export class UmbUserWorkspaceProfileSettingsElement extends UmbLitElement { + @state() + private _user?: UserResponseModel; + + @state() + private _currentUser?: UmbLoggedInUser; + + @state() + private languages: Array<{ name: string; value: string; selected: boolean }> = []; + + #authContext?: typeof UMB_AUTH.TYPE; + #userWorkspaceContext?: typeof UMB_USER_WORKSPACE_CONTEXT.TYPE; + + constructor() { + super(); + + this.consumeContext(UMB_AUTH, (instance) => { + this.#authContext = instance; + this.#observeCurrentUser(); + }); + + this.consumeContext(UMB_USER_WORKSPACE_CONTEXT, (instance) => { + this.#userWorkspaceContext = instance; + this.observe(this.#userWorkspaceContext.data, (user) => (this._user = user)); + }); + } + + #onLanguageChange(event: Event) { + const target = event.composedPath()[0] as UUISelectElement; + + if (typeof target?.value === 'string') { + this.#userWorkspaceContext?.updateProperty('languageIsoCode', target.value); + } + } + + #observeCurrentUser() { + if (!this.#authContext) return; + this.observe(this.#authContext.currentUser, async (currentUser) => { + this._currentUser = currentUser; + + if (!currentUser) { + return; + } + + // Find all translations and make a unique list of iso codes + const translations = await firstValueFrom(umbExtensionsRegistry.extensionsOfType('localization')); + + this.languages = translations + .filter((isoCode) => isoCode !== undefined) + .map((translation) => ({ + value: translation.meta.culture.toLowerCase(), + name: translation.name, + selected: false, + })); + + const currentUserLanguageCode = currentUser.languageIsoCode?.toLowerCase(); + + // Set the current user's language as selected + const currentUserLanguage = this.languages.find((language) => language.value === currentUserLanguageCode); + + if (currentUserLanguage) { + currentUserLanguage.selected = true; + } else { + // If users language code did not fit any of the options. We will create an option that fits, named unknown. + // In this way the user can keep their choice though a given language was not present at this time. + this.languages.push({ + value: currentUserLanguageCode ?? 'en-us', + name: currentUserLanguageCode ? `${currentUserLanguageCode} (unknown)` : 'Unknown', + selected: true, + }); + } + }); + } + + render() { + return html` +
Profile
+ ${this.#renderEmailProperty()} ${this.#renderUILanguageProperty()} +
`; + } + + #renderEmailProperty() { + return html` + + + + `; + } + + #renderUILanguageProperty() { + return html` + + + + + `; + } + + static styles = [ + UmbTextStyles, + css` + uui-input { + width: 100%; + } + `, + ]; +} + +export default UmbUserWorkspaceProfileSettingsElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-user-workspace-profile-settings': UmbUserWorkspaceProfileSettingsElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace-editor.element.ts index cb868cf2d1..250911eadb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace-editor.element.ts @@ -3,7 +3,7 @@ import { type UmbUserDetail } from '../index.js'; import { UmbUserWorkspaceContext } from './user-workspace.context.js'; import { type UmbUserGroupInputElement } from '@umbraco-cms/backoffice/user-group'; import { UmbUserRepository } from '@umbraco-cms/backoffice/user'; -import { UUIInputElement, UUIInputEvent, UUISelectElement } from '@umbraco-cms/backoffice/external/uui'; +import { UUIInputElement, UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; import { css, html, @@ -14,14 +14,19 @@ import { ifDefined, repeat, } from '@umbraco-cms/backoffice/external/lit'; -import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs'; import { UMB_CHANGE_PASSWORD_MODAL, type UmbModalManagerContext } from '@umbraco-cms/backoffice/modal'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { UserStateModel } from '@umbraco-cms/backoffice/backend-api'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; -import { UMB_AUTH, type UmbLoggedInUser } from '@umbraco-cms/backoffice/auth'; +import { type UmbLoggedInUser } from '@umbraco-cms/backoffice/auth'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; +import { UmbInputDocumentElement } from '@umbraco-cms/backoffice/document'; +import { UmbInputMediaElement } from '@umbraco-cms/backoffice/media'; + +// Import of local components that should only be used here +import './components/user-workspace-profile-settings/user-workspace-profile-settings.element.js'; +import './components/user-workspace-access-settings/user-workspace-access-settings.element.js'; @customElement('umb-user-workspace-editor') export class UmbUserWorkspaceEditorElement extends UmbLitElement { @@ -31,10 +36,6 @@ export class UmbUserWorkspaceEditorElement extends UmbLitElement { @state() private _user?: UmbUserDetail; - @state() - private languages: Array<{ name: string; value: string; selected: boolean }> = []; - - #auth?: typeof UMB_AUTH.TYPE; #modalContext?: UmbModalManagerContext; #workspaceContext?: UmbUserWorkspaceContext; @@ -43,11 +44,6 @@ export class UmbUserWorkspaceEditorElement extends UmbLitElement { constructor() { super(); - this.consumeContext(UMB_AUTH, (instance) => { - this.#auth = instance; - this.#observeCurrentUser(); - }); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (workspaceContext) => { this.#workspaceContext = workspaceContext as UmbUserWorkspaceContext; this.#observeUser(); @@ -59,45 +55,6 @@ export class UmbUserWorkspaceEditorElement extends UmbLitElement { this.observe(this.#workspaceContext.data, (user) => (this._user = user)); } - #observeCurrentUser() { - if (!this.#auth) return; - this.observe(this.#auth.currentUser, async (currentUser) => { - this._currentUser = currentUser; - - if (!currentUser) { - return; - } - - // Find all translations and make a unique list of iso codes - const translations = await firstValueFrom(umbExtensionsRegistry.extensionsOfType('localization')); - - this.languages = translations - .filter((isoCode) => isoCode !== undefined) - .map((translation) => ({ - value: translation.meta.culture.toLowerCase(), - name: translation.name, - selected: false, - })); - - const currentUserLanguageCode = currentUser.languageIsoCode?.toLowerCase(); - - // Set the current user's language as selected - const currentUserLanguage = this.languages.find((language) => language.value === currentUserLanguageCode); - - if (currentUserLanguage) { - currentUserLanguage.selected = true; - } else { - // If users language code did not fit any of the options. We will create an option that fits, named unknown. - // In this way the user can keep their choice though a given language was not present at this time. - this.languages.push({ - value: currentUserLanguageCode ?? 'en-us', - name: currentUserLanguageCode ? `${currentUserLanguageCode} (unknown)` : 'Unknown', - selected: true, - }); - } - }); - } - #onUserStatusChange() { if (!this._user || !this._user.id) return; @@ -110,20 +67,6 @@ export class UmbUserWorkspaceEditorElement extends UmbLitElement { } } - #onUserGroupsChange(userGroupIds: Array) { - this.#workspaceContext?.updateProperty('userGroupIds', userGroupIds); - } - - #onDocumentStartNodeChange(event: UmbChangeEvent) { - const target = event.target as UmbInputDocumentElement; - this.#workspaceContext?.updateProperty('contentStartNodeIds', target.selectedIds); - } - - #onMediaStartNodeChange(event: UmbChangeEvent) { - const target = event.target as UmbInputMediaElement; - this.#workspaceContext?.updateProperty('mediaStartNodeIds', target.selectedIds); - } - #onUserDelete() { if (!this._user || !this._user.id) return; @@ -141,14 +84,6 @@ export class UmbUserWorkspaceEditorElement extends UmbLitElement { } } - #onLanguageChange(event: Event) { - const target = event.composedPath()[0] as UUISelectElement; - - if (typeof target?.value === 'string') { - this.#workspaceContext?.updateProperty('languageIsoCode', target.value); - } - } - #onPasswordChange() { // TODO: check if current user is admin this.#modalContext?.open(UMB_CHANGE_PASSWORD_MODAL, { @@ -184,79 +119,10 @@ export class UmbUserWorkspaceEditorElement extends UmbLitElement { #renderLeftColumn() { if (!this._user) return nothing; - return html` -
Profile
- - - - - - - -
- -
Assign Access
-
- - - this.#onUserGroupsChange((e.target as UmbUserGroupInputElement).selectedIds)}> - - - - this.#workspaceContext?.updateProperty('contentStartNodeIds', e.target.value)} - slot="editor"> - - - - - - -
-
- -
- Based on the assigned groups and start nodes, the user has access to the following nodes -
- - Content - ${this.#renderContentStartNodes()} -
- Media - - - -
`; + return html` + + `; } #renderRightColumn() { @@ -366,30 +232,6 @@ export class UmbUserWorkspaceEditorElement extends UmbLitElement { return buttons; } - #renderContentStartNodes() { - if (!this._user || !this._user.contentStartNodeIds) return; - - if (this._user.contentStartNodeIds.length < 1) - return html` - - - - `; - - //TODO Render the name of the content start node instead of it's id. - return repeat( - this._user.contentStartNodeIds, - (node) => node, - (node) => { - return html` - - - - `; - }, - ); - } - static styles = [ UmbTextStyles, css` @@ -425,18 +267,7 @@ export class UmbUserWorkspaceEditorElement extends UmbLitElement { font-size: var(--uui-size-16); place-self: center; } - hr { - border: none; - border-bottom: 1px solid var(--uui-color-divider); - width: 100%; - } - uui-input { - width: 100%; - } - .faded-text { - color: var(--uui-color-text-alt); - font-size: 0.8rem; - } + uui-tag { width: fit-content; } @@ -448,10 +279,6 @@ export class UmbUserWorkspaceEditorElement extends UmbLitElement { display: flex; flex-direction: column; } - #assign-access { - display: flex; - flex-direction: column; - } `, ]; }