diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/detail/detail-repository-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/detail/detail-repository-base.ts index 9a6ff148fd..5f396ed615 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/repository/detail/detail-repository-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/detail/detail-repository-base.ts @@ -9,7 +9,7 @@ import type { UmbDetailStore } from '@umbraco-cms/backoffice/store'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; export abstract class UmbDetailRepositoryBase< - DetailModelType extends { unique: string; entityType: string; parentUnique: string | null }, + DetailModelType extends { unique: string; entityType: string; parentUnique?: string | null }, > extends UmbRepositoryBase implements UmbDetailRepository, UmbApi diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/detail/dictionary-detail.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/detail/dictionary-detail.repository.ts new file mode 100644 index 0000000000..5c531cac33 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/detail/dictionary-detail.repository.ts @@ -0,0 +1,11 @@ +import type { UmbDictionaryDetailModel } from '../../types.js'; +import { UmbDictionaryServerDataSource } from './dictionary-detail.server.data-source.js'; +import { UMB_DICTIONARY_DETAIL_STORE_CONTEXT } from './dictionary-detail.store.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbDetailRepositoryBase } from '@umbraco-cms/backoffice/repository'; + +export class UmbDictionaryDetailRepository extends UmbDetailRepositoryBase { + constructor(host: UmbControllerHost) { + super(host, UmbDictionaryServerDataSource, UMB_DICTIONARY_DETAIL_STORE_CONTEXT); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/detail/dictionary-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/detail/dictionary-detail.server.data-source.ts new file mode 100644 index 0000000000..df9c5fbcbf --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/detail/dictionary-detail.server.data-source.ts @@ -0,0 +1,150 @@ +import type { UmbDictionaryDetailModel } from '../../types.js'; +import { UMB_DICTIONARY_ENTITY_TYPE } from '../../entity.js'; +import { UmbId } from '@umbraco-cms/backoffice/id'; +import type { UmbDetailDataSource } from '@umbraco-cms/backoffice/repository'; +import type { + CreateDictionaryItemRequestModel, + DictionaryItemModelBaseModel, +} from '@umbraco-cms/backoffice/backend-api'; +import { DictionaryResource } from '@umbraco-cms/backoffice/backend-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; + +/** + * A data source for the Dictionary that fetches data from the server + * @export + * @class UmbDictionaryServerDataSource + * @implements {RepositoryDetailDataSource} + */ +export class UmbDictionaryServerDataSource implements UmbDetailDataSource { + #host: UmbControllerHost; + + /** + * Creates an instance of UmbDictionaryServerDataSource. + * @param {UmbControllerHost} host + * @memberof UmbDictionaryServerDataSource + */ + constructor(host: UmbControllerHost) { + this.#host = host; + } + + /** + * Creates a new Dictionary scaffold + * @return { CreateDictionaryRequestModel } + * @memberof UmbDictionaryServerDataSource + */ + async createScaffold() { + const data: UmbDictionaryDetailModel = { + entityType: UMB_DICTIONARY_ENTITY_TYPE, + unique: UmbId.new(), + name: '', + translations: [], + }; + + return { data }; + } + + /** + * Fetches a Dictionary with the given id from the server + * @param {string} unique + * @return {*} + * @memberof UmbDictionaryServerDataSource + */ + async read(unique: string) { + if (!unique) throw new Error('Unique is missing'); + + const { data, error } = await tryExecuteAndNotify(this.#host, DictionaryResource.getDictionaryById({ id: unique })); + + if (error || !data) { + return { error }; + } + + // TODO: make data mapper to prevent errors + const dictionary: UmbDictionaryDetailModel = { + entityType: UMB_DICTIONARY_ENTITY_TYPE, + unique: data.id, + name: data.name, + translations: data.translations, + }; + + return { data: dictionary }; + } + + /** + * Inserts a new Dictionary on the server + * @param {UmbDictionaryDetailModel} model + * @return {*} + * @memberof UmbDictionaryServerDataSource + */ + async create(model: UmbDictionaryDetailModel) { + if (!model) throw new Error('Dictionary is missing'); + + // TODO: make data mapper to prevent errors + const requestBody: CreateDictionaryItemRequestModel = { + id: model.unique, + parentId: null, + name: model.name, + translations: model.translations, + }; + + const { data, error } = await tryExecuteAndNotify( + this.#host, + DictionaryResource.postDictionary({ + requestBody, + }), + ); + + if (data) { + return this.read(data); + } + + return { error }; + } + + /** + * Updates a Dictionary on the server + * @param {UmbDictionaryDetailModel} Dictionary + * @return {*} + * @memberof UmbDictionaryServerDataSource + */ + async update(model: UmbDictionaryDetailModel) { + if (!model.unique) throw new Error('Unique is missing'); + + // TODO: make data mapper to prevent errors + const requestBody: DictionaryItemModelBaseModel = { + name: model.name, + translations: model.translations, + }; + + const { data, error } = await tryExecuteAndNotify( + this.#host, + DictionaryResource.putDictionaryById({ + id: model.unique, + requestBody, + }), + ); + + if (data) { + return this.read(data); + } + + return { error }; + } + + /** + * Deletes a Dictionary on the server + * @param {string} unique + * @return {*} + * @memberof UmbDictionaryServerDataSource + */ + async delete(unique: string) { + if (!unique) throw new Error('Unique is missing'); + + return tryExecuteAndNotify( + this.#host, + DictionaryResource.deleteDictionaryById({ + id: unique, + }), + ); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/detail/dictionary-detail.store.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/detail/dictionary-detail.store.ts new file mode 100644 index 0000000000..0aef481a6d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/detail/dictionary-detail.store.ts @@ -0,0 +1,25 @@ +import type { UmbDictionaryDetailModel } from '../../types.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; +import { UmbDetailStoreBase } from '@umbraco-cms/backoffice/store'; +import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; + +/** + * @export + * @class UmbDictionaryDetailStore + * @extends {UmbStoreBase} + * @description - Data Store for Dictionary Details + */ +export class UmbDictionaryDetailStore extends UmbDetailStoreBase { + /** + * Creates an instance of UmbDictionaryDetailStore. + * @param {UmbControllerHostElement} host + * @memberof UmbDictionaryDetailStore + */ + constructor(host: UmbControllerHostElement) { + super(host, UMB_DICTIONARY_DETAIL_STORE_CONTEXT.toString()); + } +} + +export const UMB_DICTIONARY_DETAIL_STORE_CONTEXT = new UmbContextToken( + 'UmbDictionaryDetailStore', +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/detail/index.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/detail/index.ts new file mode 100644 index 0000000000..8fa66ea678 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/detail/index.ts @@ -0,0 +1,2 @@ +export { UmbDictionaryDetailRepository } from './dictionary-detail.repository.js'; +export { UMB_DICTIONARY_DETAIL_REPOSITORY_ALIAS } from './manifests.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/detail/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/detail/manifests.ts new file mode 100644 index 0000000000..68fcac160d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/detail/manifests.ts @@ -0,0 +1,23 @@ +import { UmbDictionaryDetailRepository } from './dictionary-detail.repository.js'; +import { UmbDictionaryDetailStore } from './dictionary-detail.store.js'; +import type { ManifestRepository, ManifestStore } from '@umbraco-cms/backoffice/extension-registry'; + +export const UMB_DICTIONARY_DETAIL_REPOSITORY_ALIAS = 'Umb.Repository.Dictionary.Detail'; + +const repository: ManifestRepository = { + type: 'repository', + alias: UMB_DICTIONARY_DETAIL_REPOSITORY_ALIAS, + name: 'Dictionary Detail Repository', + api: UmbDictionaryDetailRepository, +}; + +export const UMB_DICTIONARY_DETAIL_STORE_ALIAS = 'Umb.Store.Dictionary.Detail'; + +const store: ManifestStore = { + type: 'store', + alias: UMB_DICTIONARY_DETAIL_STORE_ALIAS, + name: 'Dictionary Detail Store', + api: UmbDictionaryDetailStore, +}; + +export const manifests = [repository, store]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/dictionary.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/dictionary.repository.ts index 06babd801e..e11880be2c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/dictionary.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/dictionary.repository.ts @@ -1,14 +1,7 @@ import { type UmbDictionaryTreeStore, UMB_DICTIONARY_TREE_STORE_CONTEXT } from '../tree/index.js'; -import type { UmbDictionaryStore} from './dictionary.store.js'; -import { UMB_DICTIONARY_STORE_CONTEXT } from './dictionary.store.js'; import { UmbDictionaryDetailServerDataSource } from './sources/dictionary-detail.server.data-source.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbBaseController } from '@umbraco-cms/backoffice/class-api'; -import type { - CreateDictionaryItemRequestModel, - UpdateDictionaryItemRequestModel, -} from '@umbraco-cms/backoffice/backend-api'; -import { type UmbNotificationContext, UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; import { UmbTemporaryFileRepository } from '@umbraco-cms/backoffice/temporary-file'; @@ -18,27 +11,16 @@ export class UmbDictionaryRepository extends UmbBaseController implements UmbApi #treeStore?: UmbDictionaryTreeStore; #detailSource: UmbDictionaryDetailServerDataSource = new UmbDictionaryDetailServerDataSource(this); - #detailStore?: UmbDictionaryStore; #temporaryFileRepository: UmbTemporaryFileRepository = new UmbTemporaryFileRepository(this); - #notificationContext?: UmbNotificationContext; - constructor(host: UmbControllerHost) { super(host); this.#init = Promise.all([ - this.consumeContext(UMB_DICTIONARY_STORE_CONTEXT, (instance) => { - this.#detailStore = instance; - }), - this.consumeContext(UMB_DICTIONARY_TREE_STORE_CONTEXT, (instance) => { this.#treeStore = instance; }), - - this.consumeContext(UMB_NOTIFICATION_CONTEXT, (instance) => { - this.#notificationContext = instance; - }), ]); } @@ -47,84 +29,11 @@ export class UmbDictionaryRepository extends UmbBaseController implements UmbApi return this.#treeStore!.items(ids); } - // DETAILS - - // TODO: consider if we want to create a specific createScaffoldWithName, to loose the coupling to the model. - async createScaffold(parentId: string | null, preset?: Partial) { - if (parentId === undefined) throw new Error('Parent id is missing'); - await this.#init; - return this.#detailSource.createScaffold(parentId, preset); - } - - async requestById(id: string) { - if (!id) throw new Error('Id is missing'); - await this.#init; - - const { data, error } = await this.#detailSource.read(id); - - if (data) { - this.#detailStore?.append(data); - } - - return { data, error }; - } - - async byId(id: string) { - if (!id) throw new Error('Key is missing'); - await this.#init; - return this.#detailStore!.byId(id); - } - async list(skip = 0, take = 1000) { await this.#init; return this.#detailSource.list(skip, take); } - async delete(id: string) { - await this.#init; - await this.#treeStore?.removeItem(id); - return this.#detailSource.delete(id); - } - - async save(id: string, updatedDictionary: UpdateDictionaryItemRequestModel) { - if (!id) throw new Error('Id is missing'); - if (!updatedDictionary) throw new Error('Dictionary is missing'); - - await this.#init; - - const { error } = await this.#detailSource.update(id, updatedDictionary); - - if (!error) { - // TODO: we currently don't use the detail store for anything. - // Consider to look up the data before fetching from the server - // Consider notify a workspace if a dictionary is updated in the store while someone is editing it. - //this.#detailStore?.append(dictionary); - this.#treeStore?.updateItem(id, { name: updatedDictionary.name }); - - const notification = { data: { message: `Dictionary '${updatedDictionary.name}' saved` } }; - this.#notificationContext?.peek('positive', notification); - } - - return { error }; - } - - async create(detail: CreateDictionaryItemRequestModel) { - await this.#init; - - if (!detail.name) { - throw new Error('Name is missing'); - } - - const { data, error } = await this.#detailSource.create(detail); - - if (!error) { - const notification = { data: { message: `Dictionary '${detail.name}' created` } }; - this.#notificationContext?.peek('positive', notification); - } - - return { data, error }; - } - async export(id: string, includeChildren = false) { await this.#init; diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/types.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/types.ts index cbda0dcce8..ee61f7ee2e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/types.ts @@ -3,7 +3,6 @@ import type { UmbDictionaryEntityType } from './entity.js'; export interface UmbDictionaryDetailModel { entityType: UmbDictionaryEntityType; unique: string; - parentUnique: string | null; name: string; translations: Array; }