diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/unlock/types.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/unlock/types.ts new file mode 100644 index 0000000000..33d388bc08 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/unlock/types.ts @@ -0,0 +1,5 @@ +import { UmbDataSourceErrorResponse } from '@umbraco-cms/backoffice/repository'; + +export interface UmbUnlockUserDataSource { + unlock(userIds: string[]): Promise; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/unlock/unlock-user.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/unlock/unlock-user.repository.ts new file mode 100644 index 0000000000..4477f30f8e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/unlock/unlock-user.repository.ts @@ -0,0 +1,48 @@ +import { UMB_USER_STORE_CONTEXT_TOKEN, type UmbUserStore } from '../user.store.js'; +import { UmbUnlockUserServerDataSource } from './unlock-user.server.data.js'; +import { type UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; +import { UserStateModel } from '@umbraco-cms/backoffice/backend-api'; +import { UMB_NOTIFICATION_CONTEXT_TOKEN, UmbNotificationContext } from '@umbraco-cms/backoffice/notification'; + +export class UmbUnlockUserRepository { + #host: UmbControllerHostElement; + #init; + + #source: UmbUnlockUserServerDataSource; + #detailStore?: UmbUserStore; + #notificationContext?: UmbNotificationContext; + + constructor(host: UmbControllerHostElement) { + this.#host = host; + this.#source = new UmbUnlockUserServerDataSource(this.#host); + + this.#init = Promise.all([ + new UmbContextConsumerController(this.#host, UMB_USER_STORE_CONTEXT_TOKEN, (instance) => { + this.#detailStore = instance; + }).asPromise(), + + new UmbContextConsumerController(this.#host, UMB_NOTIFICATION_CONTEXT_TOKEN, (instance) => { + this.#notificationContext = instance; + }).asPromise(), + ]); + } + + async unlock(ids: Array) { + if (ids.length === 0) throw new Error('User ids are missing'); + await this.#init; + + const { data, error } = await this.#source.unlock(ids); + + if (!error) { + ids.forEach((id) => { + this.#detailStore?.updateItem(id, { state: UserStateModel.ACTIVE, failedPasswordAttempts: 0 }); + }); + + const notification = { data: { message: `User unlocked` } }; + this.#notificationContext?.peek('positive', notification); + } + + return { data, error }; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/unlock/unlock-user.server.data.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/unlock/unlock-user.server.data.ts new file mode 100644 index 0000000000..cd0bd649d1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/repository/unlock/unlock-user.server.data.ts @@ -0,0 +1,41 @@ +import { type UmbUnlockUserDataSource } from './types.js'; +import { UserResource } from '@umbraco-cms/backoffice/backend-api'; +import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; + +/** + * A server data source for unlocking users + * @export + * @class UmbUnlockUserServerDataSource + */ +export class UmbUnlockUserServerDataSource implements UmbUnlockUserDataSource { + #host: UmbControllerHostElement; + + /** + * Creates an instance of UmbUnlockUserServerDataSource. + * @param {UmbControllerHostElement} host + * @memberof UmbUnlockUserServerDataSource + */ + constructor(host: UmbControllerHostElement) { + this.#host = host; + } + + /** + * Unlock users + * @param {string[]} userIds + * @returns {Promise} + * @memberof UmbUnlockUserServerDataSource + */ + async unlock(userIds: string[]) { + if (!userIds) throw new Error('User ids are missing'); + + return tryExecuteAndNotify( + this.#host, + UserResource.postUserUnlock({ + requestBody: { + userIds, + }, + }), + ); + } +}