diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index ac79713592..075d0e1d0a 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -1,7 +1,7 @@ { "name": "@umbraco-cms/backoffice", "license": "MIT", - "version": "14.3.0", + "version": "15.0.0", "type": "module", "exports": { ".": null, diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts index 81aa997d72..9c5adb6e7c 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts @@ -357,6 +357,7 @@ export default { member: { createNewMember: 'Opret et nyt medlem', allMembers: 'Alle medlemmer', + kind: 'Slags', memberGroupNoProperties: 'Medlemgrupper har ingen yderligere egenskaber til redigering.', '2fa': 'Totrinsbekræftelse', duplicateMemberLogin: 'A member with this login already exists', @@ -364,6 +365,8 @@ export default { memberHasPassword: 'The member already has a password set', memberLockoutNotEnabled: 'Lockout is not enabled for this member', memberNotInGroup: "The member is not in group '%0%'", + memberKindDefault: 'Bruger', + memberKindApi: 'API Bruger', }, contentType: { copyFailed: 'Kopiering af indholdstypen fejlede', diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts index 8184bb467e..bbb8146db3 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts @@ -364,12 +364,15 @@ export default { createNewMember: 'Create a new member', allMembers: 'All Members', duplicateMemberLogin: 'A member with this login already exists', + kind: 'Kind', memberGroupNoProperties: 'Member groups have no additional properties for editing.', memberHasGroup: "The member is already in group '%0%'", memberHasPassword: 'The member already has a password set', memberLockoutNotEnabled: 'Lockout is not enabled for this member', memberNotInGroup: "The member is not in group '%0%'", '2fa': 'Two-Factor Authentication', + memberKindDefault: 'Member', + memberKindApi: 'API Member', }, contentType: { copyFailed: 'Failed to copy content type', diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/collection/repository/member-collection.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/collection/repository/member-collection.server.data-source.ts index 3bfb088260..42376a93c3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/collection/repository/member-collection.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/collection/repository/member-collection.server.data-source.ts @@ -50,6 +50,7 @@ export class UmbMemberCollectionServerDataSource implements UmbCollectionDataSou email: item.email, variants: item.variants as UmbVariantModel[], unique: item.id, + kind: item.kind, lastLoginDate: item.lastLoginDate || null, lastLockoutDate: item.lastLockoutDate || null, lastPasswordChangeDate: item.lastPasswordChangeDate || null, diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/collection/types.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/collection/types.ts index bb3b83cc3b..76018d2a04 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/collection/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/collection/types.ts @@ -1,4 +1,4 @@ -import type { UmbMemberEntityType } from '../entity.js'; +import type { UmbMemberDetailModel } from '../types.js'; export interface UmbMemberCollectionFilterModel { skip?: number; @@ -7,8 +7,5 @@ export interface UmbMemberCollectionFilterModel { filter?: string; } -export interface UmbMemberCollectionModel { - unique: string; - entityType: UmbMemberEntityType; - variants: Array; -} +// eslint-disable-next-line @typescript-eslint/no-empty-object-type +export interface UmbMemberCollectionModel extends UmbMemberDetailModel {} diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/collection/views/table/member-table-collection-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/collection/views/table/member-table-collection-view.element.ts index ea755050bc..ed4398313a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/collection/views/table/member-table-collection-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/collection/views/table/member-table-collection-view.element.ts @@ -1,10 +1,12 @@ import type { UmbMemberCollectionModel } from '../../types.js'; import { UMB_MEMBER_COLLECTION_CONTEXT } from '../../member-collection.context-token.js'; import type { UmbMemberCollectionContext } from '../../member-collection.context.js'; +import { UmbMemberKind } from '../../../utils/index.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbTableColumn, UmbTableConfig, UmbTableItem } from '@umbraco-cms/backoffice/components'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbMemberTypeItemRepository } from '@umbraco-cms/backoffice/member-type'; @customElement('umb-member-table-collection-view') export class UmbMemberTableCollectionViewElement extends UmbLitElement { @@ -19,12 +21,29 @@ export class UmbMemberTableCollectionViewElement extends UmbLitElement { name: this.localize.term('general_name'), alias: 'memberName', }, + { + name: this.localize.term('general_username'), + alias: 'memberUsername', + }, + { + name: this.localize.term('general_email'), + alias: 'memberEmail', + }, + { + name: this.localize.term('content_membertype'), + alias: 'memberType', + }, + { + name: this.localize.term('member_kind'), + alias: 'memberKind', + }, ]; @state() private _tableItems: Array = []; #collectionContext?: UmbMemberCollectionContext; + #memberTypeItemRepository = new UmbMemberTypeItemRepository(this); constructor() { super(); @@ -40,19 +59,44 @@ export class UmbMemberTableCollectionViewElement extends UmbLitElement { this.observe(this.#collectionContext.items, (items) => this.#createTableItems(items), 'umbCollectionItemsObserver'); } - #createTableItems(members: Array) { + async #createTableItems(members: Array) { + const memberTypeUniques = members.map((member) => member.memberType.unique); + const { data: memberTypes } = await this.#memberTypeItemRepository.requestItems(memberTypeUniques); + this._tableItems = members.map((member) => { // TODO: get correct variant name const name = member.variants[0].name; + const kind = + member.kind === UmbMemberKind.API + ? this.localize.term('member_memberKindApi') + : this.localize.term('member_memberKindDefault'); + + const memberType = memberTypes?.find((type) => type.unique === member.memberType.unique); return { id: member.unique, - icon: 'icon-user', + icon: memberType?.icon, data: [ { columnAlias: 'memberName', value: html`${name}`, }, + { + columnAlias: 'memberUsername', + value: member.username, + }, + { + columnAlias: 'memberEmail', + value: member.email, + }, + { + columnAlias: 'memberType', + value: memberType?.name, + }, + { + columnAlias: 'memberKind', + value: kind, + }, ], }; }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/detail/member-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/detail/member-detail.server.data-source.ts index 37303524ae..169e8e65fd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/detail/member-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/detail/member-detail.server.data-source.ts @@ -6,6 +6,7 @@ import type { CreateMemberRequestModel, UpdateMemberRequestModel } from '@umbrac import { MemberService } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { UmbMemberKind } from '../../utils/index.js'; /** * A data source for the Member that fetches data from the server @@ -42,6 +43,7 @@ export class UmbMemberServerDataSource implements UmbDetailDataSource { entityType: UMB_MEMBER_ENTITY_TYPE, unique: item.id, name: item.variants[0].name || '', + kind: item.kind, memberType: { unique: item.memberType.id, icon: item.memberType.icon, diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/item/types.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/item/types.ts index 9b58f77ef6..660b7d84fc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/item/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/item/types.ts @@ -1,4 +1,5 @@ import type { UmbMemberEntityType } from '../../entity.js'; +import type { UmbMemberKindType } from '../../utils/index.js'; import type { UmbReferenceByUnique } from '@umbraco-cms/backoffice/models'; export interface UmbMemberItemModel { @@ -11,6 +12,7 @@ export interface UmbMemberItemModel { collection: UmbReferenceByUnique | null; }; variants: Array; + kind: UmbMemberKindType; } export interface UmbMemberVariantItemModel { diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/search/member-search.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/search/member-search.server.data-source.ts index 4c8d4acdaf..febcdd024c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/search/member-search.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/search/member-search.server.data-source.ts @@ -43,6 +43,7 @@ export class UmbMemberSearchServerDataSource implements UmbSearchDataSource data?.variants[0].createDate); readonly updateDate = this.#currentData.asObservablePart((data) => data?.variants[0].updateDate); readonly contentTypeUnique = this.#currentData.asObservablePart((data) => data?.memberType.unique); + readonly kind = this.#currentData.asObservablePart((data) => data?.kind); readonly structure = new UmbContentTypeStructureManager(this, new UmbMemberTypeDetailRepository(this)); readonly varies = this.structure.ownerContentTypePart((x) => diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/member/member-workspace-view-member-info.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/member/member-workspace-view-member-info.element.ts index 2a8b77ddb3..4c3d0414c1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/member/member-workspace-view-member-info.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/member/member-workspace-view-member-info.element.ts @@ -1,5 +1,6 @@ // import { UMB_COMPOSITION_PICKER_MODAL, type UmbCompositionPickerModalData } from '../../../modals/index.js'; import { UMB_MEMBER_WORKSPACE_CONTEXT } from '../../member-workspace.context-token.js'; +import { UmbMemberKind, type UmbMemberKindType } from '../../../utils/index.js'; import { TimeFormatOptions } from './utils.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; @@ -13,8 +14,10 @@ import { UmbMemberTypeItemRepository } from '@umbraco-cms/backoffice/member-type export class UmbMemberWorkspaceViewMemberInfoElement extends UmbLitElement implements UmbWorkspaceViewElement { @state() private _memberTypeUnique = ''; + @state() private _memberTypeName = ''; + @state() private _memberTypeIcon = ''; @@ -30,6 +33,9 @@ export class UmbMemberWorkspaceViewMemberInfoElement extends UmbLitElement imple @state() private _unique = ''; + @state() + private _memberKind?: UmbMemberKindType; + #workspaceContext?: typeof UMB_MEMBER_WORKSPACE_CONTEXT.TYPE; #memberTypeItemRepository: UmbMemberTypeItemRepository = new UmbMemberTypeItemRepository(this); @@ -51,6 +57,7 @@ export class UmbMemberWorkspaceViewMemberInfoElement extends UmbLitElement imple this.observe(this.#workspaceContext.createDate, (date) => (this._createDate = this.#setDateFormat(date))); this.observe(this.#workspaceContext.updateDate, (date) => (this._updateDate = this.#setDateFormat(date))); this.observe(this.#workspaceContext.unique, (unique) => (this._unique = unique || '')); + this.observe(this.#workspaceContext.kind, (kind) => (this._memberKind = kind)); const memberType = (await this.#memberTypeItemRepository.requestItems([this._memberTypeUnique])).data?.[0]; if (!memberType) return; @@ -70,41 +77,45 @@ export class UmbMemberWorkspaceViewMemberInfoElement extends UmbLitElement imple #renderGeneralSection() { return html` -
- Created - ${this._createDate} -
-
- Last edited - ${this._updateDate} -
-
- Member Type - - - -
-
- Id - ${this._unique} -
+ +
+

Created

+ ${this._createDate} +
+
+

Last edited

+ ${this._updateDate} +
+
+

Member Type

+ + + +
+
+

+ ${this._memberKind === UmbMemberKind.API + ? this.localize.term('member_memberKindApi') + : this.localize.term('member_memberKindDefault')} +
+
+

Id

+ ${this._unique} +
+
`; } static override styles = [ UmbTextStyles, css` - .general-item { - display: flex; - flex-direction: column; - gap: var(--uui-size-space-1); - } - - .general-item:not(:last-child) { - margin-bottom: var(--uui-size-space-6); + h4 { + margin: 0; } `, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/member/member-workspace-view-member.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/member/member-workspace-view-member.element.ts index bb33e453b8..8573a9b9c4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/member/member-workspace-view-member.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/member/member-workspace-view-member.element.ts @@ -208,34 +208,36 @@ export class UmbMemberWorkspaceViewMemberElement extends UmbLitElement implement return html`
-
- Failed login attempts - ${this._workspaceContext.failedPasswordAttempts} -
-
- Last lockout date - - ${this._workspaceContext.lastLockOutDate - ? this.localize.date(this._workspaceContext.lastLockOutDate, TimeFormatOptions) - : this.localize.term('general_never')} - -
-
- Last login - - ${this._workspaceContext.lastLoginDate - ? this.localize.date(this._workspaceContext.lastLoginDate, TimeFormatOptions) - : this.localize.term('general_never')} - -
-
- Password changed - - ${this._workspaceContext.lastPasswordChangeDate - ? this.localize.date(this._workspaceContext.lastPasswordChangeDate, TimeFormatOptions) - : this.localize.term('general_never')} - -
+ +
+

Failed login attempts

+ ${this._workspaceContext.failedPasswordAttempts} +
+
+

Last lockout date

+ + ${this._workspaceContext.lastLockOutDate + ? this.localize.date(this._workspaceContext.lastLockOutDate, TimeFormatOptions) + : this.localize.term('general_never')} + +
+
+

Last login

+ + ${this._workspaceContext.lastLoginDate + ? this.localize.date(this._workspaceContext.lastLoginDate, TimeFormatOptions) + : this.localize.term('general_never')} + +
+
+

Password changed

+ + ${this._workspaceContext.lastPasswordChangeDate + ? this.localize.date(this._workspaceContext.lastPasswordChangeDate, TimeFormatOptions) + : this.localize.term('general_never')} + +
+
@@ -291,14 +293,8 @@ export class UmbMemberWorkspaceViewMemberElement extends UmbLitElement implement color: var(--uui-color-danger); } - .general-item { - display: flex; - flex-direction: column; - gap: var(--uui-size-space-1); - } - - .general-item:not(:last-child) { - margin-bottom: var(--uui-size-space-6); + h4 { + margin: 0; } `, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/components/user-workspace-info/user-workspace-info.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/components/user-workspace-info/user-workspace-info.element.ts index 3be3786c98..898bea5823 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/components/user-workspace-info/user-workspace-info.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/components/user-workspace-info/user-workspace-info.element.ts @@ -97,18 +97,20 @@ export class UmbUserWorkspaceInfoElement extends UmbLitElement { #renderInfoList() { return html` - ${repeat( - this._userInfo, - (item) => item.labelKey, - (item) => this.#renderInfoItem(item.labelKey, item.value), - )} + + ${repeat( + this._userInfo, + (item) => item.labelKey, + (item) => this.#renderInfoItem(item.labelKey, item.value), + )} + `; } #renderInfoItem(labelKey: string, value?: string | number) { return html` -