diff --git a/src/Umbraco.Web.UI.Client/.storybook/preview.js b/src/Umbraco.Web.UI.Client/.storybook/preview.js
index 78b58f1fa6..11eaeaa8ed 100644
--- a/src/Umbraco.Web.UI.Client/.storybook/preview.js
+++ b/src/Umbraco.Web.UI.Client/.storybook/preview.js
@@ -11,8 +11,11 @@ import { html } from 'lit-html';
import { initialize, mswDecorator } from 'msw-storybook-addon';
import { setCustomElements } from '@storybook/web-components';
-import { UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN, UmbDataTypeDetailStore } from '../src/backoffice/settings/data-types/data-type.detail.store';
-import { UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN, UmbDocumentTypeDetailStore } from '../src/backoffice/documents/document-types/document-type.detail.store';
+import { UmbDataTypeStore } from '../src/backoffice/settings/data-types/data-type.store';
+import {
+ UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN,
+ UmbDocumentTypeStore,
+} from '../src/backoffice/documents/document-types/document-type.detail.store';
import customElementManifests from '../custom-elements.json';
import { UmbIconStore } from '../libs/store/icon/icon.store';
@@ -55,12 +58,19 @@ customElements.define('umb-storybook', UmbStoryBookElement);
const storybookProvider = (story) => html` ${story()} `;
+// TODO: Stop using this context provider element. If we need to continue this path, then we should make a new element which just has a create method that can be used to spin up code. This is because our ContextAPIs provide them self. so no need for a provider element. just a element.
const dataTypeStoreProvider = (story) => html`
- new UmbDataTypeDetailStore(host)}>${story()}
+ new UmbDataTypeStore(host)}
+ >${story()}
`;
const documentTypeStoreProvider = (story) => html`
- new UmbDocumentTypeDetailStore(host)}
+ new UmbDocumentTypeStore(host)}
>${story()}
`;
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/index.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/index.ts
index 4c1a6ad54a..c3cbdcd186 100644
--- a/src/Umbraco.Web.UI.Client/libs/backend-api/src/index.ts
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/index.ts
@@ -12,7 +12,15 @@ export type { AssemblyModel } from './models/AssemblyModel';
export { CallingConventionsModel } from './models/CallingConventionsModel';
export type { ConsentLevelModel } from './models/ConsentLevelModel';
export type { ConstructorInfoModel } from './models/ConstructorInfoModel';
+export { ContentStateModel } from './models/ContentStateModel';
export type { ContentTreeItemModel } from './models/ContentTreeItemModel';
+export type { ContentTypeCleanupModel } from './models/ContentTypeCleanupModel';
+export type { ContentTypeCompositionModel } from './models/ContentTypeCompositionModel';
+export { ContentTypeCompositionTypeModel } from './models/ContentTypeCompositionTypeModel';
+export type { ContentTypeSortModel } from './models/ContentTypeSortModel';
+export type { ContentTypeViewModelBaseDocumentTypePropertyTypeDocumentTypePropertyTypeContainerModel } from './models/ContentTypeViewModelBaseDocumentTypePropertyTypeDocumentTypePropertyTypeContainerModel';
+export type { ContentUrlInfoModel } from './models/ContentUrlInfoModel';
+export type { ContentViewModelBaseDocumentPropertyDocumentVariantModel } from './models/ContentViewModelBaseDocumentPropertyDocumentVariantModel';
export type { CreatedResultModel } from './models/CreatedResultModel';
export type { CultureModel } from './models/CultureModel';
export type { CustomAttributeDataModel } from './models/CustomAttributeDataModel';
@@ -20,9 +28,11 @@ export type { CustomAttributeNamedArgumentModel } from './models/CustomAttribute
export type { CustomAttributeTypedArgumentModel } from './models/CustomAttributeTypedArgumentModel';
export type { DatabaseInstallModel } from './models/DatabaseInstallModel';
export type { DatabaseSettingsModel } from './models/DatabaseSettingsModel';
+export type { DataTypeCopyModel } from './models/DataTypeCopyModel';
export type { DataTypeCreateModel } from './models/DataTypeCreateModel';
export type { DataTypeModel } from './models/DataTypeModel';
export type { DataTypeModelBaseModel } from './models/DataTypeModelBaseModel';
+export type { DataTypeMoveModel } from './models/DataTypeMoveModel';
export type { DataTypePropertyModel } from './models/DataTypePropertyModel';
export type { DataTypePropertyReferenceModel } from './models/DataTypePropertyReferenceModel';
export type { DataTypeReferenceModel } from './models/DataTypeReferenceModel';
@@ -36,12 +46,19 @@ export type { DictionaryItemModelBaseModel } from './models/DictionaryItemModelB
export type { DictionaryItemsImportModel } from './models/DictionaryItemsImportModel';
export type { DictionaryItemTranslationModel } from './models/DictionaryItemTranslationModel';
export type { DictionaryItemUpdateModel } from './models/DictionaryItemUpdateModel';
+export type { DictionaryMoveModel } from './models/DictionaryMoveModel';
export type { DictionaryOverviewModel } from './models/DictionaryOverviewModel';
export type { DictionaryUploadModel } from './models/DictionaryUploadModel';
export { DirectionModel } from './models/DirectionModel';
export type { DocumentBlueprintTreeItemModel } from './models/DocumentBlueprintTreeItemModel';
+export type { DocumentModel } from './models/DocumentModel';
+export type { DocumentPropertyModel } from './models/DocumentPropertyModel';
export type { DocumentTreeItemModel } from './models/DocumentTreeItemModel';
+export type { DocumentTypeModel } from './models/DocumentTypeModel';
+export type { DocumentTypePropertyTypeContainerModel } from './models/DocumentTypePropertyTypeContainerModel';
+export type { DocumentTypePropertyTypeModel } from './models/DocumentTypePropertyTypeModel';
export type { DocumentTypeTreeItemModel } from './models/DocumentTypeTreeItemModel';
+export type { DocumentVariantModel } from './models/DocumentVariantModel';
export type { EncoderFallbackModel } from './models/EncoderFallbackModel';
export type { EncodingModel } from './models/EncodingModel';
export type { EntityTreeItemModel } from './models/EntityTreeItemModel';
@@ -138,6 +155,11 @@ export type { ProblemDetailsModel } from './models/ProblemDetailsModel';
export type { ProfilingStatusModel } from './models/ProfilingStatusModel';
export { PropertyAttributesModel } from './models/PropertyAttributesModel';
export type { PropertyInfoModel } from './models/PropertyInfoModel';
+export type { PropertyTypeAppearanceModel } from './models/PropertyTypeAppearanceModel';
+export type { PropertyTypeContainerViewModelBaseModel } from './models/PropertyTypeContainerViewModelBaseModel';
+export type { PropertyTypeValidationModel } from './models/PropertyTypeValidationModel';
+export type { PropertyTypeViewModelBaseModel } from './models/PropertyTypeViewModelBaseModel';
+export type { PropertyViewModelBaseModel } from './models/PropertyViewModelBaseModel';
export type { ReadOnlySpanByteModel } from './models/ReadOnlySpanByteModel';
export type { RecycleBinItemModel } from './models/RecycleBinItemModel';
export { RedirectStatusModel } from './models/RedirectStatusModel';
@@ -182,6 +204,7 @@ export type { UmbracoJsonTypeInfoResolverModel } from './models/UmbracoJsonTypeI
export type { UpgradeSettingsModel } from './models/UpgradeSettingsModel';
export type { UserInstallModel } from './models/UserInstallModel';
export type { UserSettingsModel } from './models/UserSettingsModel';
+export type { VariantViewModelBaseModel } from './models/VariantViewModelBaseModel';
export type { VersionModel } from './models/VersionModel';
export { CultureResource } from './services/CultureResource';
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentStateModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentStateModel.ts
new file mode 100644
index 0000000000..f5dfe28698
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentStateModel.ts
@@ -0,0 +1,10 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export enum ContentStateModel {
+ NOT_CREATED = 'NotCreated',
+ DRAFT = 'Draft',
+ PUBLISHED = 'Published',
+ PUBLISHED_PENDING_CHANGES = 'PublishedPendingChanges',
+}
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentTypeCleanupModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentTypeCleanupModel.ts
new file mode 100644
index 0000000000..7ea186e255
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentTypeCleanupModel.ts
@@ -0,0 +1,10 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export type ContentTypeCleanupModel = {
+ preventCleanup?: boolean;
+ keepAllVersionsNewerThanDays?: number | null;
+ keepLatestVersionPerDayForDays?: number | null;
+};
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentTypeCompositionModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentTypeCompositionModel.ts
new file mode 100644
index 0000000000..eefcd22a19
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentTypeCompositionModel.ts
@@ -0,0 +1,11 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+import type { ContentTypeCompositionTypeModel } from './ContentTypeCompositionTypeModel';
+
+export type ContentTypeCompositionModel = {
+ key?: string;
+ compositionType?: ContentTypeCompositionTypeModel;
+};
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentTypeCompositionTypeModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentTypeCompositionTypeModel.ts
new file mode 100644
index 0000000000..38bd4959c5
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentTypeCompositionTypeModel.ts
@@ -0,0 +1,8 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export enum ContentTypeCompositionTypeModel {
+ COMPOSITION = 'Composition',
+ INHERITANCE = 'Inheritance',
+}
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentTypeSortModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentTypeSortModel.ts
new file mode 100644
index 0000000000..63b6a44a11
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentTypeSortModel.ts
@@ -0,0 +1,9 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export type ContentTypeSortModel = {
+ key?: string;
+ sortOrder?: number;
+};
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentTypeViewModelBaseDocumentTypePropertyTypeDocumentTypePropertyTypeContainerModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentTypeViewModelBaseDocumentTypePropertyTypeDocumentTypePropertyTypeContainerModel.ts
new file mode 100644
index 0000000000..f4edc901a6
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentTypeViewModelBaseDocumentTypePropertyTypeDocumentTypePropertyTypeContainerModel.ts
@@ -0,0 +1,27 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+import type { ContentTypeCleanupModel } from './ContentTypeCleanupModel';
+import type { ContentTypeCompositionModel } from './ContentTypeCompositionModel';
+import type { ContentTypeSortModel } from './ContentTypeSortModel';
+import type { DocumentTypePropertyTypeContainerModel } from './DocumentTypePropertyTypeContainerModel';
+import type { DocumentTypePropertyTypeModel } from './DocumentTypePropertyTypeModel';
+
+export type ContentTypeViewModelBaseDocumentTypePropertyTypeDocumentTypePropertyTypeContainerModel = {
+ key?: string;
+ alias?: string;
+ name?: string;
+ description?: string | null;
+ icon?: string;
+ allowedAsRoot?: boolean;
+ variesByCulture?: boolean;
+ variesBySegment?: boolean;
+ isElement?: boolean;
+ properties?: Array;
+ containers?: Array;
+ allowedContentTypes?: Array;
+ compositions?: Array;
+ cleanup?: ContentTypeCleanupModel;
+};
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentUrlInfoModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentUrlInfoModel.ts
new file mode 100644
index 0000000000..aabc5314c5
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentUrlInfoModel.ts
@@ -0,0 +1,9 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export type ContentUrlInfoModel = {
+ culture?: string | null;
+ url?: string;
+};
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentViewModelBaseDocumentPropertyDocumentVariantModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentViewModelBaseDocumentPropertyDocumentVariantModel.ts
new file mode 100644
index 0000000000..2ca16a2b7f
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/ContentViewModelBaseDocumentPropertyDocumentVariantModel.ts
@@ -0,0 +1,14 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+import type { DocumentPropertyModel } from './DocumentPropertyModel';
+import type { DocumentVariantModel } from './DocumentVariantModel';
+
+export type ContentViewModelBaseDocumentPropertyDocumentVariantModel = {
+ key?: string;
+ contentTypeKey?: string;
+ properties?: Array;
+ variants?: Array;
+};
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DataTypeCopyModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DataTypeCopyModel.ts
new file mode 100644
index 0000000000..163d127437
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DataTypeCopyModel.ts
@@ -0,0 +1,8 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export type DataTypeCopyModel = {
+ targetKey?: string | null;
+};
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DataTypeMoveModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DataTypeMoveModel.ts
new file mode 100644
index 0000000000..2c8513a405
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DataTypeMoveModel.ts
@@ -0,0 +1,8 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export type DataTypeMoveModel = {
+ targetKey?: string | null;
+};
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DictionaryMoveModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DictionaryMoveModel.ts
new file mode 100644
index 0000000000..c58b2262a1
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DictionaryMoveModel.ts
@@ -0,0 +1,8 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export type DictionaryMoveModel = {
+ targetKey?: string | null;
+};
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DocumentModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DocumentModel.ts
new file mode 100644
index 0000000000..2c6cb2d843
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DocumentModel.ts
@@ -0,0 +1,12 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+import type { ContentUrlInfoModel } from './ContentUrlInfoModel';
+import type { ContentViewModelBaseDocumentPropertyDocumentVariantModel } from './ContentViewModelBaseDocumentPropertyDocumentVariantModel';
+
+export type DocumentModel = (ContentViewModelBaseDocumentPropertyDocumentVariantModel & {
+ urls?: Array;
+ templateKey?: string | null;
+});
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DocumentPropertyModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DocumentPropertyModel.ts
new file mode 100644
index 0000000000..53258b68bc
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DocumentPropertyModel.ts
@@ -0,0 +1,8 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+import type { PropertyViewModelBaseModel } from './PropertyViewModelBaseModel';
+
+export type DocumentPropertyModel = PropertyViewModelBaseModel;
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DocumentTypeModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DocumentTypeModel.ts
new file mode 100644
index 0000000000..6885b3e0c3
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DocumentTypeModel.ts
@@ -0,0 +1,11 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+import type { ContentTypeViewModelBaseDocumentTypePropertyTypeDocumentTypePropertyTypeContainerModel } from './ContentTypeViewModelBaseDocumentTypePropertyTypeDocumentTypePropertyTypeContainerModel';
+
+export type DocumentTypeModel = (ContentTypeViewModelBaseDocumentTypePropertyTypeDocumentTypePropertyTypeContainerModel & {
+ allowedTemplateKeys?: Array;
+ defaultTemplateKey?: string | null;
+});
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DocumentTypePropertyTypeContainerModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DocumentTypePropertyTypeContainerModel.ts
new file mode 100644
index 0000000000..69737e93e0
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DocumentTypePropertyTypeContainerModel.ts
@@ -0,0 +1,8 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+import type { PropertyTypeContainerViewModelBaseModel } from './PropertyTypeContainerViewModelBaseModel';
+
+export type DocumentTypePropertyTypeContainerModel = PropertyTypeContainerViewModelBaseModel;
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DocumentTypePropertyTypeModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DocumentTypePropertyTypeModel.ts
new file mode 100644
index 0000000000..6fa071b539
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DocumentTypePropertyTypeModel.ts
@@ -0,0 +1,8 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+import type { PropertyTypeViewModelBaseModel } from './PropertyTypeViewModelBaseModel';
+
+export type DocumentTypePropertyTypeModel = PropertyTypeViewModelBaseModel;
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DocumentVariantModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DocumentVariantModel.ts
new file mode 100644
index 0000000000..b2d7431caa
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/DocumentVariantModel.ts
@@ -0,0 +1,12 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+import type { ContentStateModel } from './ContentStateModel';
+import type { VariantViewModelBaseModel } from './VariantViewModelBaseModel';
+
+export type DocumentVariantModel = (VariantViewModelBaseModel & {
+ state?: ContentStateModel;
+ publishDate?: string | null;
+});
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/PropertyTypeAppearanceModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/PropertyTypeAppearanceModel.ts
new file mode 100644
index 0000000000..e8138d4992
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/PropertyTypeAppearanceModel.ts
@@ -0,0 +1,8 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export type PropertyTypeAppearanceModel = {
+ labelOnTop?: boolean;
+};
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/PropertyTypeContainerViewModelBaseModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/PropertyTypeContainerViewModelBaseModel.ts
new file mode 100644
index 0000000000..7c8e755156
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/PropertyTypeContainerViewModelBaseModel.ts
@@ -0,0 +1,12 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export type PropertyTypeContainerViewModelBaseModel = {
+ key?: string;
+ parentKey?: string | null;
+ name?: string | null;
+ type?: string;
+ sortOrder?: number;
+};
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/PropertyTypeValidationModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/PropertyTypeValidationModel.ts
new file mode 100644
index 0000000000..6966054e27
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/PropertyTypeValidationModel.ts
@@ -0,0 +1,11 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export type PropertyTypeValidationModel = {
+ mandatory?: boolean;
+ mandatoryMessage?: string | null;
+ regEx?: string | null;
+ regExMessage?: string | null;
+};
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/PropertyTypeViewModelBaseModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/PropertyTypeViewModelBaseModel.ts
new file mode 100644
index 0000000000..7b3306b549
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/PropertyTypeViewModelBaseModel.ts
@@ -0,0 +1,18 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+import type { PropertyTypeAppearanceModel } from './PropertyTypeAppearanceModel';
+import type { PropertyTypeValidationModel } from './PropertyTypeValidationModel';
+
+export type PropertyTypeViewModelBaseModel = {
+ key?: string;
+ containerKey?: string | null;
+ alias?: string;
+ name?: string;
+ description?: string | null;
+ dataTypeKey?: string;
+ validation?: PropertyTypeValidationModel;
+ appearance?: PropertyTypeAppearanceModel;
+};
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/PropertyViewModelBaseModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/PropertyViewModelBaseModel.ts
new file mode 100644
index 0000000000..6a1747a826
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/PropertyViewModelBaseModel.ts
@@ -0,0 +1,11 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export type PropertyViewModelBaseModel = {
+ culture?: string | null;
+ segment?: string | null;
+ alias?: string;
+ value?: any;
+};
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/VariantViewModelBaseModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/VariantViewModelBaseModel.ts
new file mode 100644
index 0000000000..6e2f7e0a05
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/VariantViewModelBaseModel.ts
@@ -0,0 +1,12 @@
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+
+export type VariantViewModelBaseModel = {
+ culture?: string | null;
+ segment?: string | null;
+ name?: string;
+ createDate?: string;
+ updateDate?: string;
+};
+
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/DataTypeResource.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/DataTypeResource.ts
index 85c9bc56e9..2fbd392e9f 100644
--- a/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/DataTypeResource.ts
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/DataTypeResource.ts
@@ -1,8 +1,10 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
+import type { DataTypeCopyModel } from '../models/DataTypeCopyModel';
import type { DataTypeCreateModel } from '../models/DataTypeCreateModel';
import type { DataTypeModel } from '../models/DataTypeModel';
+import type { DataTypeMoveModel } from '../models/DataTypeMoveModel';
import type { DataTypeReferenceModel } from '../models/DataTypeReferenceModel';
import type { DataTypeUpdateModel } from '../models/DataTypeUpdateModel';
import type { DocumentTypeTreeItemModel } from '../models/DocumentTypeTreeItemModel';
@@ -108,6 +110,56 @@ export class DataTypeResource {
});
}
+ /**
+ * @returns any Created
+ * @throws ApiError
+ */
+ public static postDataTypeByKeyCopy({
+ key,
+ requestBody,
+ }: {
+ key: string,
+ requestBody?: DataTypeCopyModel,
+ }): CancelablePromise {
+ return __request(OpenAPI, {
+ method: 'POST',
+ url: '/umbraco/management/api/v1/data-type/{key}/copy',
+ path: {
+ 'key': key,
+ },
+ body: requestBody,
+ mediaType: 'application/json',
+ errors: {
+ 404: `Not Found`,
+ },
+ });
+ }
+
+ /**
+ * @returns any Success
+ * @throws ApiError
+ */
+ public static postDataTypeByKeyMove({
+ key,
+ requestBody,
+ }: {
+ key: string,
+ requestBody?: DataTypeMoveModel,
+ }): CancelablePromise {
+ return __request(OpenAPI, {
+ method: 'POST',
+ url: '/umbraco/management/api/v1/data-type/{key}/move',
+ path: {
+ 'key': key,
+ },
+ body: requestBody,
+ mediaType: 'application/json',
+ errors: {
+ 404: `Not Found`,
+ },
+ });
+ }
+
/**
* @returns any Success
* @throws ApiError
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/DictionaryResource.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/DictionaryResource.ts
index 63e92fb803..fd8242eca7 100644
--- a/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/DictionaryResource.ts
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/DictionaryResource.ts
@@ -5,6 +5,7 @@ import type { DictionaryImportModel } from '../models/DictionaryImportModel';
import type { DictionaryItemCreateModel } from '../models/DictionaryItemCreateModel';
import type { DictionaryItemModel } from '../models/DictionaryItemModel';
import type { DictionaryItemUpdateModel } from '../models/DictionaryItemUpdateModel';
+import type { DictionaryMoveModel } from '../models/DictionaryMoveModel';
import type { DictionaryUploadModel } from '../models/DictionaryUploadModel';
import type { DocumentTypeTreeItemModel } from '../models/DocumentTypeTreeItemModel';
import type { FolderTreeItemModel } from '../models/FolderTreeItemModel';
@@ -155,6 +156,32 @@ export class DictionaryResource {
});
}
+ /**
+ * @returns any Success
+ * @throws ApiError
+ */
+ public static postDictionaryByKeyMove({
+ key,
+ requestBody,
+ }: {
+ key: string,
+ requestBody?: DictionaryMoveModel,
+ }): CancelablePromise {
+ return __request(OpenAPI, {
+ method: 'POST',
+ url: '/umbraco/management/api/v1/dictionary/{key}/move',
+ path: {
+ 'key': key,
+ },
+ body: requestBody,
+ mediaType: 'application/json',
+ errors: {
+ 400: `Bad Request`,
+ 404: `Not Found`,
+ },
+ });
+ }
+
/**
* @returns any Created
* @throws ApiError
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/DocumentResource.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/DocumentResource.ts
index 3908c3ed85..470fa0033c 100644
--- a/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/DocumentResource.ts
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/DocumentResource.ts
@@ -1,6 +1,7 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
+import type { DocumentModel } from '../models/DocumentModel';
import type { DocumentTreeItemModel } from '../models/DocumentTreeItemModel';
import type { PagedDocumentTreeItemModel } from '../models/PagedDocumentTreeItemModel';
import type { PagedRecycleBinItemModel } from '../models/PagedRecycleBinItemModel';
@@ -11,6 +12,27 @@ import { request as __request } from '../core/request';
export class DocumentResource {
+ /**
+ * @returns any Success
+ * @throws ApiError
+ */
+ public static getDocumentByKey({
+ key,
+ }: {
+ key: string,
+ }): CancelablePromise {
+ return __request(OpenAPI, {
+ method: 'GET',
+ url: '/umbraco/management/api/v1/document/{key}',
+ path: {
+ 'key': key,
+ },
+ errors: {
+ 404: `Not Found`,
+ },
+ });
+ }
+
/**
* @returns PagedRecycleBinItemModel Success
* @throws ApiError
diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/DocumentTypeResource.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/DocumentTypeResource.ts
index 8bb02eaeb8..a36a6081cc 100644
--- a/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/DocumentTypeResource.ts
+++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/DocumentTypeResource.ts
@@ -1,6 +1,7 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
+import type { DocumentTypeModel } from '../models/DocumentTypeModel';
import type { DocumentTypeTreeItemModel } from '../models/DocumentTypeTreeItemModel';
import type { PagedDocumentTypeTreeItemModel } from '../models/PagedDocumentTypeTreeItemModel';
@@ -10,6 +11,27 @@ import { request as __request } from '../core/request';
export class DocumentTypeResource {
+ /**
+ * @returns any Success
+ * @throws ApiError
+ */
+ public static getDocumentTypeByKey({
+ key,
+ }: {
+ key: string,
+ }): CancelablePromise {
+ return __request(OpenAPI, {
+ method: 'GET',
+ url: '/umbraco/management/api/v1/document-type/{key}',
+ path: {
+ 'key': key,
+ },
+ errors: {
+ 404: `Not Found`,
+ },
+ });
+ }
+
/**
* @returns PagedDocumentTypeTreeItemModel Success
* @throws ApiError
diff --git a/src/Umbraco.Web.UI.Client/libs/models/index.ts b/src/Umbraco.Web.UI.Client/libs/models/index.ts
index df5bf0d1e9..7dfcad5f6a 100644
--- a/src/Umbraco.Web.UI.Client/libs/models/index.ts
+++ b/src/Umbraco.Web.UI.Client/libs/models/index.ts
@@ -1,14 +1,9 @@
-import { Observable } from 'rxjs';
import {
ContentTreeItemModel,
- DocumentTreeItemModel,
- DocumentTypeTreeItemModel,
EntityTreeItemModel,
FolderTreeItemModel,
- PagedEntityTreeItemModel,
ProblemDetailsModel,
} from '@umbraco-cms/backend-api';
-import { UmbControllerHostInterface } from '@umbraco-cms/controller';
// Extension Manifests
export * from '@umbraco-cms/extensions-registry';
@@ -67,25 +62,20 @@ export interface UserGroupDetails extends UserGroupEntity {
permissions: Array;
}
+/*
// Data Types
export interface DataTypeDetails extends FolderTreeItemModel {
key: string; // TODO: Remove this when the backend is fixed
- propertyEditorModelAlias: string | null;
- propertyEditorUIAlias: string | null;
- data: Array;
+ propertyEditorAlias: string | null;
+ propertyEditorUiAlias: string | null;
+ data: Array;
}
-export interface DataTypePropertyData {
+export interface DataTypeProperty {
alias: string;
value: any;
}
-
-// Document Types
-export interface DocumentTypeDetails extends DocumentTypeTreeItemModel {
- key: string; // TODO: Remove this when the backend is fixed
- alias: string;
- properties: [];
-}
+*/
// TODO: Make sure Entity Type/interface.
export interface MemberTypeDetails extends EntityTreeItemModel {
@@ -107,16 +97,6 @@ export interface ContentPropertyData {
value: any;
}
-// Documents
-export interface DocumentDetails extends DocumentTreeItemModel {
- key: string; // TODO: Remove this when the backend is fixed
- isTrashed: boolean; // TODO: remove only temp part of refactor
- properties: Array;
- data: Array;
- variants: Array; // TODO: define variant data
- //layout?: any; // TODO: define layout type - make it non-optional
-}
-
// Media
export interface MediaDetails extends ContentTreeItemModel {
key: string; // TODO: Remove this when the backend is fixed
diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/index.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/index.ts
index d823d82739..dbce65cec6 100644
--- a/src/Umbraco.Web.UI.Client/libs/observable-api/index.ts
+++ b/src/Umbraco.Web.UI.Client/libs/observable-api/index.ts
@@ -5,5 +5,6 @@ export * from './string-state';
export * from './deep-state';
export * from './array-state';
export * from './object-state';
-export * from './create-observable-part.method'
-export * from './append-to-frozen-array.method'
+export * from './create-observable-part.method';
+export * from './append-to-frozen-array.method';
+export * from './partial-update-frozen-array.method';
diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/partial-update-frozen-array.method.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/partial-update-frozen-array.method.ts
new file mode 100644
index 0000000000..14a2e4c5b6
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/libs/observable-api/partial-update-frozen-array.method.ts
@@ -0,0 +1,24 @@
+/**
+ * @export
+ * @method partialUpdateFrozenArray
+ * @param {Observable} source - RxJS Subject to use for this Observable.
+ * @param {(mappable: T) => R} mappingFunction - Method to return the part for this Observable to return.
+ * @param {(previousResult: R, currentResult: R) => boolean} [memoizationFunction] - Method to Compare if the data has changed. Should return true when data is different.
+ * @description - Creates a RxJS Observable from RxJS Subject.
+ * @example
Example append new entry for a ArrayState or a part of DeepState/ObjectState it which is an array. Where the key is unique and the item will be updated if matched with existing.
+ * const partialEntry = {value: 'myValue'};
+ * const newDataSet = partialUpdateFrozenArray(mySubject.getValue(), partialEntry, x => x.key === 'myKey');
+ * mySubject.next(newDataSet);
+ */
+export function partialUpdateFrozenArray(
+ data: T[],
+ partialEntry: Partial,
+ findMethod: (entry: T) => boolean
+): T[] {
+ const unFrozenDataSet = [...data];
+ const indexToReplace = unFrozenDataSet.findIndex((x) => findMethod(x));
+ if (indexToReplace !== -1) {
+ unFrozenDataSet[indexToReplace] = { ...unFrozenDataSet[indexToReplace], ...partialEntry };
+ }
+ return unFrozenDataSet;
+}
diff --git a/src/Umbraco.Web.UI.Client/libs/repository/detail-repository.interface.ts b/src/Umbraco.Web.UI.Client/libs/repository/detail-repository.interface.ts
index cdbf3b415d..ff6e58a689 100644
--- a/src/Umbraco.Web.UI.Client/libs/repository/detail-repository.interface.ts
+++ b/src/Umbraco.Web.UI.Client/libs/repository/detail-repository.interface.ts
@@ -6,7 +6,7 @@ export interface UmbDetailRepository {
error?: ProblemDetailsModel;
}>;
- requestDetails(key: string): Promise<{
+ requestByKey(key: string): Promise<{
data?: DetailType;
error?: ProblemDetailsModel;
}>;
diff --git a/src/Umbraco.Web.UI.Client/libs/router/router-slot.element.ts b/src/Umbraco.Web.UI.Client/libs/router/router-slot.element.ts
index fa5c50fc39..cd0299e547 100644
--- a/src/Umbraco.Web.UI.Client/libs/router/router-slot.element.ts
+++ b/src/Umbraco.Web.UI.Client/libs/router/router-slot.element.ts
@@ -1,7 +1,6 @@
-import 'router-slot';
+import { IRoute, RouterSlot } from 'router-slot';
import { LitElement, PropertyValueMap } from 'lit';
import { customElement, property } from 'lit/decorators.js';
-import { IRoute, RouterSlot } from 'router-slot';
import { UmbRouterSlotChangeEvent, UmbRouterSlotInitEvent } from '@umbraco-cms/router';
/**
@@ -40,12 +39,11 @@ export class UmbRouterSlotElement extends LitElement {
constructor() {
super();
- this.#router = document.createElement('router-slot');
+ this.#router = new RouterSlot();
// Note: I decided not to use the local changestate event, because it is not fired when the route is changed from any router-slot. And for now I wanted to keep it local.
//this.#router.addEventListener('changestate', this._onNavigationChanged);
}
-
connectedCallback() {
super.connectedCallback();
if (this.#listening === false) {
@@ -60,7 +58,6 @@ export class UmbRouterSlotElement extends LitElement {
this.#listening = false;
}
-
protected firstUpdated(_changedProperties: PropertyValueMap | Map): void {
super.firstUpdated(_changedProperties);
this._routerPath = this.#router.constructAbsolutePath('') || '';
diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json
index 0c2a2748d2..f9e93e12c7 100644
--- a/src/Umbraco.Web.UI.Client/package.json
+++ b/src/Umbraco.Web.UI.Client/package.json
@@ -42,7 +42,7 @@
"format": "prettier 'src/**/*.ts'",
"format:fix": "npm run format -- --write",
"generate:api": "openapi --input https://raw.githubusercontent.com/umbraco/Umbraco-CMS/v13/dev/src/Umbraco.Cms.Api.Management/OpenApi.json --output libs/backend-api/src --postfix Resource --useOptions",
- "generate:api-dev": "openapi --input http://localhost:9000/umbraco/swagger/v1/swagger.json --output libs/backend-api/src --postfix Resource --useOptions",
+ "generate:api-dev": "openapi --input http://localhost:11000/umbraco/swagger/v1/swagger.json --output libs/backend-api/src --postfix Resource --useOptions",
"storybook": "npm run wc-analyze && start-storybook -p 6006",
"storybook:build": "npm run wc-analyze && build-storybook",
"build-storybook": "npm run wc-analyze && build-storybook",
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts
index 014049966a..829b1ea34e 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts
@@ -15,11 +15,11 @@ import {
UmbBackofficeContext,
UMB_BACKOFFICE_CONTEXT_TOKEN,
} from './shared/components/backoffice-frame/backoffice.context';
-import { UmbDocumentTypeDetailStore } from './documents/document-types/document-type.detail.store';
-import { UmbDocumentTypeTreeStore } from './documents/document-types/document-type.tree.store';
+import { UmbDocumentTypeStore } from './documents/document-types/repository/document-type.store';
+import { UmbDocumentTypeTreeStore } from './documents/document-types/repository/document-type.tree.store';
import { UmbMediaTypeDetailStore } from './media/media-types/media-type.detail.store';
import { UmbMediaTypeTreeStore } from './media/media-types/media-type.tree.store';
-import { UmbDocumentDetailStore } from './documents/documents/repository/document.detail.store';
+import { UmbDocumentStore } from './documents/documents/repository/document.store';
import { UmbDocumentTreeStore } from './documents/documents/repository/document.tree.store';
import { UmbMediaDetailStore } from './media/media/repository/media.detail.store';
import { UmbMediaTreeStore } from './media/media/repository/media.tree.store';
@@ -33,7 +33,7 @@ import { UmbDictionaryDetailStore } from './translation/dictionary/dictionary.de
import { UmbDictionaryTreeStore } from './translation/dictionary/dictionary.tree.store';
import { UmbDocumentBlueprintDetailStore } from './documents/document-blueprints/document-blueprint.detail.store';
import { UmbDocumentBlueprintTreeStore } from './documents/document-blueprints/document-blueprint.tree.store';
-import { UmbDataTypeDetailStore } from './settings/data-types/data-type.detail.store';
+import { UmbDataTypeStore } from './settings/data-types/repository/data-type.store';
import { UmbDataTypeTreeStore } from './settings/data-types/tree/data-type.tree.store';
import { UmbTemplateTreeStore } from './templating/templates/tree/data/template.tree.store';
import { UmbTemplateDetailStore } from './templating/templates/workspace/data/template.detail.store';
@@ -82,16 +82,16 @@ export class UmbBackofficeElement extends UmbLitElement {
// TODO: find a way this is possible outside this element. It needs to be possible to register stores in extensions
this.provideContext(UMB_CURRENT_USER_STORE_CONTEXT_TOKEN, new UmbCurrentUserStore());
- new UmbDocumentDetailStore(this);
+ new UmbDocumentStore(this);
new UmbDocumentTreeStore(this);
new UmbMediaDetailStore(this);
new UmbMediaTreeStore(this);
- new UmbDataTypeDetailStore(this);
+ new UmbDataTypeStore(this);
new UmbDataTypeTreeStore(this);
new UmbUserStore(this);
new UmbMediaTypeDetailStore(this);
new UmbMediaTypeTreeStore(this);
- new UmbDocumentTypeDetailStore(this);
+ new UmbDocumentTypeStore(this);
new UmbDocumentTypeTreeStore(this);
new UmbMemberTypeDetailStore(this);
new UmbMemberTypeTreeStore(this);
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts
deleted file mode 100644
index f557f02763..0000000000
--- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-import type { DocumentTypeDetails } from '@umbraco-cms/models';
-import { UmbContextToken } from '@umbraco-cms/context-api';
-import { ArrayState } from '@umbraco-cms/observable-api';
-import { UmbEntityDetailStore, UmbStoreBase } from '@umbraco-cms/store';
-import { UmbControllerHostInterface } from '@umbraco-cms/controller';
-
-export const UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken(
- 'UmbDocumentTypeDetailStore'
-);
-
-/**
- * @export
- * @class UmbDocumentTypeDetailStore
- * @extends {UmbStoreBase}
- * @description - Details Data Store for Document Types
- */
-export class UmbDocumentTypeDetailStore extends UmbStoreBase implements UmbEntityDetailStore {
- #data = new ArrayState([], (x) => x.key);
-
- constructor(host: UmbControllerHostInterface) {
- super(host, UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString());
- }
-
- getScaffold(entityType: string, parentKey: string | null) {
- return {
- key: '',
- name: '',
- icon: '',
- type: '',
- hasChildren: false,
- parentKey: '',
- alias: '',
- properties: [],
- } as DocumentTypeDetails;
- }
-
- /**
- * @description - Request a Data Type by key. The Data Type is added to the store and is returned as an Observable.
- * @param {string} key
- * @return {*} {(Observable)}
- * @memberof UmbDocumentTypesStore
- */
- getByKey(key: string) {
- // TODO: use backend cli when available.
- fetch(`/umbraco/management/api/v1/document-type/details/${key}`)
- .then((res) => res.json())
- .then((data) => {
- this.#data.append(data);
- });
-
- return this.#data.getObservablePart((documentTypes) =>
- documentTypes.find((documentType) => documentType.key === key)
- );
- }
-
- // TODO: make sure UI somehow can follow the status of this action.
- /**
- * @description - Save a Data Type.
- * @param {Array} documentTypes
- * @memberof UmbDocumentTypesStore
- * @return {*} {Promise}
- */
- save(data: DocumentTypeDetails[]) {
- // fetch from server and update store
- // TODO: use Fetcher API.
- let body: string;
-
- try {
- body = JSON.stringify(data);
- } catch (error) {
- console.error(error);
- return Promise.reject();
- }
-
- // TODO: use backend cli when available.
- return fetch('/umbraco/management/api/v1/document-type/save', {
- method: 'POST',
- body: body,
- headers: {
- 'Content-Type': 'application/json',
- },
- })
- .then((res) => res.json())
- .then((data: Array) => {
- this.#data.append(data);
- });
- }
-
- // TODO: How can we avoid having this in both stores?
- /**
- * @description - Delete a Data Type.
- * @param {string[]} keys
- * @memberof UmbDocumentTypesStore
- * @return {*} {Promise}
- */
- async delete(keys: string[]) {
- // TODO: use backend cli when available.
- await fetch('/umbraco/backoffice/document-type/delete', {
- method: 'POST',
- body: JSON.stringify(keys),
- headers: {
- 'Content-Type': 'application/json',
- },
- });
-
- this.#data.remove(keys);
- }
-}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.tree.store.ts
deleted file mode 100644
index af1d44bb99..0000000000
--- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.tree.store.ts
+++ /dev/null
@@ -1,91 +0,0 @@
-import { DocumentTypeResource, DocumentTreeItemModel } from '@umbraco-cms/backend-api';
-import { tryExecuteAndNotify } from '@umbraco-cms/resources';
-import { UmbContextToken } from '@umbraco-cms/context-api';
-import { ArrayState } from '@umbraco-cms/observable-api';
-import { UmbStoreBase } from '@umbraco-cms/store';
-import type { UmbControllerHostInterface } from '@umbraco-cms/controller';
-
-export const UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken(
- 'UmbDocumentTypeTreeStore'
-);
-
-/**
- * @export
- * @class UmbDocumentTypeTreeStore
- * @extends {UmbStoreBase}
- * @description - Tree Data Store for Data Types
- */
-export class UmbDocumentTypeTreeStore extends UmbStoreBase {
- #data = new ArrayState([], (x) => x.key);
-
- constructor(host: UmbControllerHostInterface) {
- super(host, UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN.toString());
- }
-
- // TODO: How can we avoid having this in both stores?
- /**
- * @description - Delete a Data Type.
- * @param {string[]} keys
- * @memberof UmbDocumentTypesStore
- * @return {*} {Promise}
- */
- async delete(keys: string[]) {
- // TODO: use backend cli when available.
- await fetch('/umbraco/backoffice/document-type/delete', {
- method: 'POST',
- body: JSON.stringify(keys),
- headers: {
- 'Content-Type': 'application/json',
- },
- });
-
- this.#data.remove(keys);
- }
-
- getTreeRoot() {
- tryExecuteAndNotify(this._host, DocumentTypeResource.getTreeDocumentTypeRoot({})).then(({ data }) => {
- if (data) {
- // TODO: how do we handle if an item has been removed during this session(like in another tab or by another user)?
- this.#data.append(data.items);
- }
- });
-
- // TODO: remove ignore when we know how to handle trashed items.
- return this.#data.getObservablePart((items) => items.filter((item) => item.parentKey === null));
- }
-
- getTreeItemChildren(key: string) {
- tryExecuteAndNotify(
- this._host,
- DocumentTypeResource.getTreeDocumentTypeChildren({
- parentKey: key,
- })
- ).then(({ data }) => {
- if (data) {
- // TODO: how do we handle if an item has been removed during this session(like in another tab or by another user)?
- this.#data.append(data.items);
- }
- });
-
- // TODO: remove ignore when we know how to handle trashed items.
- return this.#data.getObservablePart((items) => items.filter((item) => item.parentKey === key));
- }
-
- getTreeItems(keys: Array) {
- if (keys?.length > 0) {
- tryExecuteAndNotify(
- this._host,
- DocumentTypeResource.getTreeDocumentTypeItem({
- key: keys,
- })
- ).then(({ data }) => {
- if (data) {
- // TODO: how do we handle if an item has been removed during this session(like in another tab or by another user)?
- this.#data.append(data);
- }
- });
- }
-
- return this.#data.getObservablePart((items) => items.filter((item) => keys.includes(item.key ?? '')));
- }
-}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.repository.ts
new file mode 100644
index 0000000000..f296ca5bdb
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.repository.ts
@@ -0,0 +1,222 @@
+import type { RepositoryTreeDataSource } from '../../../../../libs/repository/repository-tree-data-source.interface';
+import { DocumentTypeTreeServerDataSource } from './sources/document-type.tree.server.data';
+import { UmbDocumentTypeServerDataSource } from './sources/document-type.server.data';
+import { UmbDocumentTypeTreeStore, UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN } from './document-type.tree.store';
+import { UmbDocumentTypeStore, UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN } from './document-type.store';
+import { UmbControllerHostInterface } from '@umbraco-cms/controller';
+import { UmbContextConsumerController } from '@umbraco-cms/context-api';
+import { ProblemDetailsModel, DocumentTypeModel } from '@umbraco-cms/backend-api';
+import type { UmbTreeRepository } from 'libs/repository/tree-repository.interface';
+import { UmbDetailRepository } from '@umbraco-cms/repository';
+import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/notification';
+
+type ItemType = DocumentTypeModel;
+
+// Move to documentation / JSdoc
+/* We need to create a new instance of the repository from within the element context. We want the notifications to be displayed in the right context. */
+// element -> context -> repository -> (store) -> data source
+// All methods should be async and return a promise. Some methods might return an observable as part of the promise response.
+export class UmbDocumentTypeRepository implements UmbTreeRepository, UmbDetailRepository {
+ #init!: Promise;
+
+ #host: UmbControllerHostInterface;
+
+ #treeSource: RepositoryTreeDataSource;
+ #treeStore?: UmbDocumentTypeTreeStore;
+
+ #detailDataSource: UmbDocumentTypeServerDataSource;
+ #detailStore?: UmbDocumentTypeStore;
+
+ #notificationService?: UmbNotificationService;
+
+ constructor(host: UmbControllerHostInterface) {
+ this.#host = host;
+
+ // TODO: figure out how spin up get the correct data source
+ this.#treeSource = new DocumentTypeTreeServerDataSource(this.#host);
+ this.#detailDataSource = new UmbDocumentTypeServerDataSource(this.#host);
+
+ this.#init = Promise.all([
+ new UmbContextConsumerController(this.#host, UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN, (instance) => {
+ this.#treeStore = instance;
+ }),
+
+ new UmbContextConsumerController(this.#host, UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN, (instance) => {
+ this.#detailStore = instance;
+ }),
+
+ new UmbContextConsumerController(this.#host, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN, (instance) => {
+ this.#notificationService = instance;
+ }),
+ ]);
+ }
+
+ // TODO: Trash
+ // TODO: Move
+
+ async requestRootTreeItems() {
+ await this.#init;
+
+ const { data, error } = await this.#treeSource.getRootItems();
+
+ if (data) {
+ this.#treeStore?.appendItems(data.items);
+ }
+
+ return { data, error, asObservable: () => this.#treeStore!.rootItems };
+ }
+
+ async requestTreeItemsOf(parentKey: string | null) {
+ await this.#init;
+
+ if (!parentKey) {
+ const error: ProblemDetailsModel = { title: 'Parent key is missing' };
+ return { data: undefined, error };
+ }
+
+ const { data, error } = await this.#treeSource.getChildrenOf(parentKey);
+
+ if (data) {
+ this.#treeStore?.appendItems(data.items);
+ }
+
+ return { data, error, asObservable: () => this.#treeStore!.childrenOf(parentKey) };
+ }
+
+ async requestTreeItems(keys: Array) {
+ await this.#init;
+
+ if (!keys) {
+ const error: ProblemDetailsModel = { title: 'Keys are missing' };
+ return { data: undefined, error };
+ }
+
+ const { data, error } = await this.#treeSource.getItems(keys);
+
+ return { data, error, asObservable: () => this.#treeStore!.items(keys) };
+ }
+
+ async rootTreeItems() {
+ await this.#init;
+ return this.#treeStore!.rootItems;
+ }
+
+ async treeItemsOf(parentKey: string | null) {
+ await this.#init;
+ return this.#treeStore!.childrenOf(parentKey);
+ }
+
+ async treeItems(keys: Array) {
+ await this.#init;
+ return this.#treeStore!.items(keys);
+ }
+
+ // DETAILS:
+
+ async createDetailsScaffold(parentKey: string | null) {
+ await this.#init;
+
+ if (!parentKey) {
+ throw new Error('Parent key is missing');
+ }
+
+ return this.#detailDataSource.createScaffold(parentKey);
+ }
+
+ async requestByKey(key: string) {
+ await this.#init;
+
+ // TODO: should we show a notification if the key is missing?
+ // Investigate what is best for Acceptance testing, cause in that perspective a thrown error might be the best choice?
+ if (!key) {
+ const error: ProblemDetailsModel = { title: 'Key is missing' };
+ return { error };
+ }
+ const { data, error } = await this.#detailDataSource.get(key);
+
+ if (data) {
+ this.#detailStore?.append(data);
+ }
+
+ return { data, error };
+ }
+
+ async byKey(key: string) {
+ await this.#init;
+ return this.#detailStore!.byKey(key);
+ }
+
+ // Could potentially be general methods:
+
+ async createDetail(template: ItemType) {
+ await this.#init;
+
+ if (!template || !template.key) {
+ throw new Error('Template is missing');
+ }
+
+ const { error } = await this.#detailDataSource.insert(template);
+
+ if (!error) {
+ const notification = { data: { message: `Document created` } };
+ this.#notificationService?.peek('positive', notification);
+ }
+
+ // TODO: we currently don't use the detail store for anything.
+ // Consider to look up the data before fetching from the server
+ this.#detailStore?.append(template);
+ // TODO: Update tree store with the new item? or ask tree to request the new item?
+
+ return { error };
+ }
+
+ async saveDetail(item: ItemType) {
+ await this.#init;
+
+ if (!item || !item.key) {
+ throw new Error('Document-Type is missing');
+ }
+
+ const { error } = await this.#detailDataSource.update(item);
+
+ if (!error) {
+ const notification = { data: { message: `Document saved` } };
+ this.#notificationService?.peek('positive', notification);
+ }
+
+ // 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 template is updated in the store while someone is editing it.
+ this.#detailStore?.append(item);
+ this.#treeStore?.updateItem(item.key, { name: item.name });
+ // TODO: would be nice to align the stores on methods/methodNames.
+
+ return { error };
+ }
+
+ // General:
+
+ async delete(key: string) {
+ await this.#init;
+
+ if (!key) {
+ throw new Error('Document key is missing');
+ }
+
+ const { error } = await this.#detailDataSource.delete(key);
+
+ if (!error) {
+ const notification = { data: { message: `Document deleted` } };
+ this.#notificationService?.peek('positive', notification);
+ }
+
+ // 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 template is deleted from the store while someone is editing it.
+ this.#detailStore?.remove([key]);
+ this.#treeStore?.removeItem(key);
+ // TODO: would be nice to align the stores on methods/methodNames.
+
+ return { error };
+ }
+}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.store.ts
new file mode 100644
index 0000000000..1906d62454
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.store.ts
@@ -0,0 +1,55 @@
+import { DocumentTypeModel } from '@umbraco-cms/backend-api';
+import { UmbContextToken } from '@umbraco-cms/context-api';
+import { ArrayState } from '@umbraco-cms/observable-api';
+import { UmbStoreBase } from '@umbraco-cms/store';
+import { UmbControllerHostInterface } from '@umbraco-cms/controller';
+
+/**
+ * @export
+ * @class UmbDocumentTypeStore
+ * @extends {UmbStoreBase}
+ * @description - Data Store for Document Types
+ */
+export class UmbDocumentTypeStore extends UmbStoreBase {
+ #data = new ArrayState([], (x) => x.key);
+
+ /**
+ * Creates an instance of UmbDocumentTypeStore.
+ * @param {UmbControllerHostInterface} host
+ * @memberof UmbDocumentTypeStore
+ */
+ constructor(host: UmbControllerHostInterface) {
+ super(host, UmbDocumentTypeStore.name);
+ }
+
+ /**
+ * Append a document-type to the store
+ * @param {DocumentTypeModel} document
+ * @memberof UmbDocumentTypeStore
+ */
+ append(document: DocumentTypeModel) {
+ this.#data.append([document]);
+ }
+
+ /**
+ * Append a document-type to the store
+ * @param {DocumentTypeModel} document
+ * @memberof UmbDocumentTypeStore
+ */
+ byKey(key: DocumentTypeModel['key']) {
+ return this.#data.getObservablePart((x) => x.find((y) => y.key === key));
+ }
+
+ /**
+ * Removes document-types in the store with the given uniques
+ * @param {string[]} uniques
+ * @memberof UmbDocumentTypeStore
+ */
+ remove(uniques: Array) {
+ this.#data.remove(uniques);
+ }
+}
+
+export const UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN = new UmbContextToken(
+ UmbDocumentTypeStore.name
+);
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.tree.store.ts
new file mode 100644
index 0000000000..becaa5def9
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.tree.store.ts
@@ -0,0 +1,93 @@
+import { EntityTreeItemModel } from '@umbraco-cms/backend-api';
+import { UmbContextToken } from '@umbraco-cms/context-api';
+import { ArrayState } from '@umbraco-cms/observable-api';
+import { UmbStoreBase } from '@umbraco-cms/store';
+import { UmbControllerHostInterface } from '@umbraco-cms/controller';
+
+/**
+ * @export
+ * @class UmbDocumentTypeTreeStore
+ * @extends {UmbStoreBase}
+ * @description - Tree Data Store for Document-Types
+ */
+// TODO: consider if tree store could be turned into a general EntityTreeStore class?
+export class UmbDocumentTypeTreeStore extends UmbStoreBase {
+ #data = new ArrayState([], (x) => x.key);
+
+ /**
+ * Creates an instance of UmbDocumentTypeTreeStore.
+ * @param {UmbControllerHostInterface} host
+ * @memberof UmbDocumentTypeTreeStore
+ */
+ constructor(host: UmbControllerHostInterface) {
+ super(host, UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN.toString());
+ }
+
+ /**
+ * Appends items to the store
+ * @param {Array} items
+ * @memberof UmbDocumentTypeTreeStore
+ */
+ appendItems(items: Array) {
+ this.#data.append(items);
+ }
+
+ /**
+ * Updates an item in the store
+ * @param {string} key
+ * @param {Partial} data
+ * @memberof UmbDocumentTypeTreeStore
+ */
+ updateItem(key: string, data: Partial) {
+ const entries = this.#data.getValue();
+ const entry = entries.find((entry) => entry.key === key);
+
+ if (entry) {
+ this.#data.appendOne({ ...entry, ...data });
+ }
+ }
+
+ /**
+ * Removes an item from the store
+ * @param {string} key
+ * @memberof UmbDocumentTypeTreeStore
+ */
+ removeItem(key: string) {
+ const entries = this.#data.getValue();
+ const entry = entries.find((entry) => entry.key === key);
+
+ if (entry) {
+ this.#data.remove([key]);
+ }
+ }
+
+ /**
+ * An observable to observe the root items
+ * @memberof UmbDocumentTypeTreeStore
+ */
+ rootItems = this.#data.getObservablePart((items) => items.filter((item) => item.parentKey === null));
+
+ /**
+ * Returns an observable to observe the children of a given parent
+ * @param {(string | null)} parentKey
+ * @return {*}
+ * @memberof UmbDocumentTypeTreeStore
+ */
+ childrenOf(parentKey: string | null) {
+ return this.#data.getObservablePart((items) => items.filter((item) => item.parentKey === parentKey));
+ }
+
+ /**
+ * Returns an observable to observe the items with the given keys
+ * @param {Array} keys
+ * @return {*}
+ * @memberof UmbDocumentTypeTreeStore
+ */
+ items(keys: Array) {
+ return this.#data.getObservablePart((items) => items.filter((item) => keys.includes(item.key ?? '')));
+ }
+}
+
+export const UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken(
+ UmbDocumentTypeTreeStore.name
+);
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/manifests.ts
new file mode 100644
index 0000000000..89c4abbb74
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/manifests.ts
@@ -0,0 +1,13 @@
+import { UmbDocumentTypeRepository } from './document-type.repository';
+import { ManifestRepository } from 'libs/extensions-registry/repository.models';
+
+export const DOCUMENT_TYPE_REPOSITORY_ALIAS = 'Umb.Repository.DocumentTypes';
+
+const repository: ManifestRepository = {
+ type: 'repository',
+ alias: DOCUMENT_TYPE_REPOSITORY_ALIAS,
+ name: 'Document Types Repository',
+ class: UmbDocumentTypeRepository,
+};
+
+export const manifests = [repository];
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/sources/document-type.server.data.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/sources/document-type.server.data.ts
new file mode 100644
index 0000000000..3bb0855410
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/sources/document-type.server.data.ts
@@ -0,0 +1,179 @@
+import { RepositoryDetailDataSource } from '@umbraco-cms/repository';
+import { DocumentTypeResource, ProblemDetailsModel, DocumentTypeModel } from '@umbraco-cms/backend-api';
+import { UmbControllerHostInterface } from '@umbraco-cms/controller';
+import { tryExecuteAndNotify } from '@umbraco-cms/resources';
+
+/**
+ * A data source for the Document Type that fetches data from the server
+ * @export
+ * @class UmbDocumentTypeServerDataSource
+ * @implements {RepositoryDetailDataSource}
+ */
+export class UmbDocumentTypeServerDataSource implements RepositoryDetailDataSource {
+ #host: UmbControllerHostInterface;
+
+ /**
+ * Creates an instance of UmbDocumentServerDataSource.
+ * @param {UmbControllerHostInterface} host
+ * @memberof UmbDocumentServerDataSource
+ */
+ constructor(host: UmbControllerHostInterface) {
+ this.#host = host;
+ }
+
+ /**
+ * Fetches a Document with the given key from the server
+ * @param {string} key
+ * @return {*}
+ * @memberof UmbDocumentTypeServerDataSource
+ */
+ async get(key: string) {
+ if (!key) {
+ const error: ProblemDetailsModel = { title: 'Key is missing' };
+ return { error };
+ }
+
+ return tryExecuteAndNotify(
+ this.#host,
+ DocumentTypeResource.getDocumentTypeByKey({
+ key,
+ })
+ );
+ }
+
+ /**
+ * Creates a new Document scaffold
+ * @param {(string | null)} parentKey
+ * @return {*}
+ * @memberof UmbDocumentTypeServerDataSource
+ */
+ async createScaffold(parentKey: string | null) {
+ const data: DocumentTypeModel = {
+ properties: [],
+ };
+
+ return { data };
+ }
+
+ /**
+ * Inserts a new Document on the server
+ * @param {Document} document
+ * @return {*}
+ * @memberof UmbDocumentTypeServerDataSource
+ */
+ async insert(document: DocumentTypeModel) {
+ if (!document.key) {
+ //const error: ProblemDetails = { title: 'Document key is missing' };
+ return Promise.reject();
+ }
+ //const payload = { key: document.key, requestBody: document };
+
+ let body: string;
+
+ try {
+ body = JSON.stringify(document);
+ } catch (error) {
+ console.error(error);
+ return Promise.reject();
+ }
+ //return tryExecuteAndNotify(this.#host, DocumentTypeResource.postDocument(payload));
+ // TODO: use resources when end point is ready:
+ return tryExecuteAndNotify(
+ this.#host,
+ fetch('/umbraco/management/api/v1/document/save', {
+ method: 'POST',
+ body: body,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ }) as any
+ );
+ }
+
+ /**
+ * Updates a Document on the server
+ * @param {Document} Document
+ * @return {*}
+ * @memberof UmbDocumentTypeServerDataSource
+ */
+ // TODO: Error mistake in this:
+ async update(document: DocumentTypeModel) {
+ if (!document.key) {
+ const error: ProblemDetailsModel = { title: 'Document key is missing' };
+ return { error };
+ }
+ //const payload = { key: document.key, requestBody: document };
+
+ let body: string;
+
+ try {
+ body = JSON.stringify(document);
+ } catch (error) {
+ const myError: ProblemDetailsModel = { title: 'JSON could not parse' };
+ return { error: myError };
+ }
+
+ // TODO: use resources when end point is ready:
+ return tryExecuteAndNotify(
+ this.#host,
+ fetch('/umbraco/management/api/v1/document-type/save', {
+ method: 'POST',
+ body: body,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ }) as any
+ );
+ }
+
+ /**
+ * Trash a Document on the server
+ * @param {Document} Document
+ * @return {*}
+ * @memberof UmbDocumentTypeServerDataSource
+ */
+ async trash(key: string) {
+ if (!key) {
+ const error: ProblemDetailsModel = { title: 'Key is missing' };
+ return { error };
+ }
+
+ // TODO: use resources when end point is ready:
+ return tryExecuteAndNotify(
+ this.#host,
+ fetch('/umbraco/management/api/v1/document-type/trash', {
+ method: 'POST',
+ body: JSON.stringify([key]),
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ }) as any
+ );
+ }
+
+ /**
+ * Deletes a Template on the server
+ * @param {string} key
+ * @return {*}
+ * @memberof UmbDocumentTypeServerDataSource
+ */
+ // TODO: Error mistake in this:
+ async delete(key: string) {
+ if (!key) {
+ const error: ProblemDetailsModel = { title: 'Key is missing' };
+ return { error };
+ }
+
+ // TODO: use resources when end point is ready:
+ return tryExecuteAndNotify(
+ this.#host,
+ fetch('/umbraco/management/api/v1/document-type/trash', {
+ method: 'POST',
+ body: JSON.stringify([key]),
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ })
+ );
+ }
+}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/sources/document-type.tree.server.data.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/sources/document-type.tree.server.data.ts
new file mode 100644
index 0000000000..8e4b1fefeb
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/sources/document-type.tree.server.data.ts
@@ -0,0 +1,101 @@
+import type { RepositoryTreeDataSource } from '../../../../../../libs/repository/repository-tree-data-source.interface';
+import { ProblemDetailsModel, DocumentTypeResource } from '@umbraco-cms/backend-api';
+import { UmbControllerHostInterface } from '@umbraco-cms/controller';
+import { tryExecuteAndNotify } from '@umbraco-cms/resources';
+
+/**
+ * A data source for the Document tree that fetches data from the server
+ * @export
+ * @class DocumentTreeServerDataSource
+ * @implements {DocumentTreeDataSource}
+ */
+export class DocumentTypeTreeServerDataSource implements RepositoryTreeDataSource {
+ #host: UmbControllerHostInterface;
+
+ // TODO: how do we handle trashed items?
+ async trashItems(keys: Array) {
+ // TODO: use backend cli when available.
+ return tryExecuteAndNotify(
+ this.#host,
+ fetch('/umbraco/management/api/v1/document-type/trash', {
+ method: 'POST',
+ body: JSON.stringify(keys),
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ })
+ );
+ }
+
+ async moveItems(keys: Array, destination: string) {
+ // TODO: use backend cli when available.
+ return tryExecuteAndNotify(
+ this.#host,
+ fetch('/umbraco/management/api/v1/document-type/move', {
+ method: 'POST',
+ body: JSON.stringify({ keys, destination }),
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ })
+ );
+ }
+
+ /**
+ * Creates an instance of DocumentTreeServerDataSource.
+ * @param {UmbControllerHostInterface} host
+ * @memberof DocumentTreeServerDataSource
+ */
+ constructor(host: UmbControllerHostInterface) {
+ this.#host = host;
+ }
+
+ /**
+ * Fetches the root items for the tree from the server
+ * @return {*}
+ * @memberof DocumentTreeServerDataSource
+ */
+ async getRootItems() {
+ return tryExecuteAndNotify(this.#host, DocumentTypeResource.getTreeDocumentTypeRoot({}));
+ }
+
+ /**
+ * Fetches the children of a given parent key from the server
+ * @param {(string | null)} parentKey
+ * @return {*}
+ * @memberof DocumentTreeServerDataSource
+ */
+ async getChildrenOf(parentKey: string | null) {
+ if (!parentKey) {
+ const error: ProblemDetailsModel = { title: 'Parent key is missing' };
+ return { error };
+ }
+
+ return tryExecuteAndNotify(
+ this.#host,
+ DocumentTypeResource.getTreeDocumentTypeChildren({
+ parentKey,
+ })
+ );
+ }
+
+ /**
+ * Fetches the items for the given keys from the server
+ * @param {Array} keys
+ * @return {*}
+ * @memberof DocumentTreeServerDataSource
+ */
+ async getItems(keys: Array) {
+ if (keys) {
+ const error: ProblemDetailsModel = { title: 'Keys are missing' };
+ return { error };
+ }
+
+ return tryExecuteAndNotify(
+ this.#host,
+ DocumentTypeResource.getTreeDocumentTypeItem({
+ key: keys,
+ })
+ );
+ }
+}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/tree/manifests.ts
index f0b8d07917..111abd7e5c 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/tree/manifests.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/tree/manifests.ts
@@ -1,4 +1,4 @@
-import { UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN } from '../document-type.detail.store';
+import { UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN } from '../repository/document-type.store';
import type { ManifestTree, ManifestTreeItemAction } from '@umbraco-cms/models';
const tree: ManifestTree = {
@@ -6,7 +6,7 @@ const tree: ManifestTree = {
alias: 'Umb.Tree.DocumentTypes',
name: 'Document Types Tree',
meta: {
- storeAlias: UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString(),
+ storeAlias: UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN.toString(),
},
};
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.context.ts
index 949cb7f5e4..da36b169cb 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.context.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.context.ts
@@ -1,22 +1,23 @@
import { UmbEntityWorkspaceManager } from '../../../shared/components/workspace/workspace-context/entity-manager-controller';
import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context';
import { UmbWorkspaceEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-entity-context.interface';
-import { UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN } from '../document-type.detail.store';
-import type { DocumentTypeDetails } from '@umbraco-cms/models';
+import { UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN } from '../repository/document-type.store';
+import type { DocumentTypeModel } from '@umbraco-cms/backend-api';
-export class UmbWorkspaceDocumentTypeContext extends UmbWorkspaceContext implements UmbWorkspaceEntityContextInterface {
-
- #manager = new UmbEntityWorkspaceManager(this._host, 'document-type', UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN);
+export class UmbWorkspaceDocumentTypeContext
+ extends UmbWorkspaceContext
+ implements UmbWorkspaceEntityContextInterface
+{
+ #manager = new UmbEntityWorkspaceManager(this._host, 'document-type', UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN);
public readonly data = this.#manager.state.asObservable();
public readonly name = this.#manager.state.getObservablePart((state) => state?.name);
-
setName(name: string) {
- this.#manager.state.update({name: name})
+ this.#manager.state.update({ name: name });
}
setIcon(icon: string) {
- this.#manager.state.update({icon: icon})
+ this.#manager.state.update({ icon: icon });
}
getEntityType = this.#manager.getEntityType;
getUnique = this.#manager.getEntityKey;
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.element.ts
index f2aabc22ff..460eecd5ac 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.element.ts
@@ -1,13 +1,12 @@
import { UUIInputElement, UUIInputEvent } from '@umbraco-ui/uui';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { css, html } from 'lit';
-import { customElement, property, state } from 'lit/decorators.js';
-import { distinctUntilChanged } from 'rxjs';
+import { customElement, state } from 'lit/decorators.js';
+import type { UmbWorkspaceEntityElement } from '../../../shared/components/workspace/workspace-entity-element.interface';
import { UmbWorkspaceDocumentTypeContext } from './document-type-workspace.context';
-import type { DocumentTypeDetails } from '@umbraco-cms/models';
-import { UmbModalService, UMB_MODAL_SERVICE_CONTEXT_TOKEN } from 'src/core/modal';
+import type { DocumentTypeModel } from '@umbraco-cms/backend-api';
import { UmbLitElement } from '@umbraco-cms/element';
-import type { UmbWorkspaceEntityElement } from 'src/backoffice/shared/components/workspace/workspace-entity-element.interface';
+import { UmbModalService, UMB_MODAL_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/modal';
@customElement('umb-document-type-workspace')
export class UmbDocumentTypeWorkspaceElement extends UmbLitElement implements UmbWorkspaceEntityElement {
@@ -46,11 +45,10 @@ export class UmbDocumentTypeWorkspaceElement extends UmbLitElement implements Um
name: 'umb:document-dashed-line',
};
-
private _workspaceContext: UmbWorkspaceDocumentTypeContext = new UmbWorkspaceDocumentTypeContext(this);
@state()
- private _documentType?: DocumentTypeDetails;
+ private _documentType?: DocumentTypeModel;
private _modalService?: UmbModalService;
@@ -61,9 +59,9 @@ export class UmbDocumentTypeWorkspaceElement extends UmbLitElement implements Um
this._modalService = instance;
});
- this.observe(this._workspaceContext.data.pipe(distinctUntilChanged()), (data) => {
- // TODO: make method to identify if data is of type DocumentTypeDetails
- this._documentType = data as DocumentTypeDetails;
+ this.observe(this._workspaceContext.data, (data) => {
+ // TODO: make method to identify if data is of type DocumentType
+ this._documentType = data as DocumentType;
});
}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.stories.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.stories.ts
index 10c7815fe5..576ba9435f 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.stories.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.stories.ts
@@ -2,7 +2,7 @@ import './document-type-workspace.element';
import { Meta, Story } from '@storybook/web-components';
import { html } from 'lit-html';
import type { UmbDocumentTypeWorkspaceElement } from './document-type-workspace.element';
-import { data } from 'src/core/mocks/data/document-type.data';
+import { treeData } from 'src/core/mocks/data/document-type.data';
export default {
title: 'Workspaces/Document Type',
@@ -11,5 +11,5 @@ export default {
} as Meta;
export const AAAOverview: Story = () =>
- html` `;
+ html` `;
AAAOverview.storyName = 'Overview';
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/views/design/workspace-view-document-type-design.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/views/design/workspace-view-document-type-design.element.ts
index 1c845de142..4520f8b82f 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/views/design/workspace-view-document-type-design.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/views/design/workspace-view-document-type-design.element.ts
@@ -1,17 +1,16 @@
import { css, html } from 'lit';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { customElement, state } from 'lit/decorators.js';
-import { distinctUntilChanged } from 'rxjs';
import { UmbWorkspaceDocumentTypeContext } from '../../document-type-workspace.context';
import { UmbLitElement } from '@umbraco-cms/element';
-import type { DocumentTypeDetails } from '@umbraco-cms/models';
+import type { DocumentTypeModel } from '@umbraco-cms/backend-api';
@customElement('umb-workspace-view-document-type-design')
export class UmbWorkspaceViewDocumentTypeDesignElement extends UmbLitElement {
static styles = [UUITextStyles, css``];
@state()
- _documentType?: DocumentTypeDetails | null;
+ _documentType?: DocumentTypeModel;
private _workspaceContext?: UmbWorkspaceDocumentTypeContext;
@@ -28,7 +27,7 @@ export class UmbWorkspaceViewDocumentTypeDesignElement extends UmbLitElement {
private _observeDocumentType() {
if (!this._workspaceContext) return;
- this.observe(this._workspaceContext.data.pipe(distinctUntilChanged()), (documentType) => {
+ this.observe(this._workspaceContext.data, (documentType) => {
this._documentType = documentType;
});
}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/collection/views/table/column-layouts/document-table-actions-column-layout.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/collection/views/table/column-layouts/document-table-actions-column-layout.element.ts
index 41061206fe..a2bd997906 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/collection/views/table/column-layouts/document-table-actions-column-layout.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/collection/views/table/column-layouts/document-table-actions-column-layout.element.ts
@@ -1,8 +1,8 @@
import { css, html, LitElement, nothing } from 'lit';
import { ifDefined } from 'lit-html/directives/if-defined.js';
import { customElement, property, state } from 'lit/decorators.js';
-import { UmbExecutedEvent } from 'src/core/events';
import type { UmbTableColumn, UmbTableItem } from '../../../../../../shared/components/table';
+import { UmbExecutedEvent } from 'src/core/events';
// TODO: this could be done more generic, but for now we just need it for the document table
@customElement('umb-document-table-actions-column-layout')
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/collection/views/table/document-table-collection-view.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/collection/views/table/document-table-collection-view.element.ts
index 63b8e85bee..e669b51c40 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/collection/views/table/document-table-collection-view.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/collection/views/table/document-table-collection-view.element.ts
@@ -14,13 +14,12 @@ import {
UmbTableOrderedEvent,
UmbTableSelectedEvent,
} from '../../../../../shared/components/table';
-import type { DocumentDetails } from '@umbraco-cms/models';
import { UmbLitElement } from '@umbraco-cms/element';
-import { EntityTreeItemModel } from '@umbraco-cms/backend-api';
+import { DocumentTreeItemModel, EntityTreeItemModel } from '@umbraco-cms/backend-api';
import './column-layouts/document-table-actions-column-layout.element';
-type EntityType = DocumentDetails;
+type EntityType = DocumentTreeItemModel;
@customElement('umb-document-table-collection-view')
export class UmbDocumentTableCollectionViewElement extends UmbLitElement {
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.repository.ts
index 41ceac4b52..7c0149a51f 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.repository.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.repository.ts
@@ -1,23 +1,22 @@
import type { RepositoryTreeDataSource } from '../../../../../libs/repository/repository-tree-data-source.interface';
import { DocumentTreeServerDataSource } from './sources/document.tree.server.data';
import { UmbDocumentTreeStore, UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN } from './document.tree.store';
-import { UmbDocumentDetailStore, UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN } from './document.detail.store';
-import { UmbDocumentDetailServerDataSource } from './sources/document.detail.server.data';
+import { UmbDocumentStore, UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN } from './document.store';
+import { UmbDocumentServerDataSource } from './sources/document.server.data';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { UmbContextConsumerController } from '@umbraco-cms/context-api';
-import { ProblemDetailsModel } from '@umbraco-cms/backend-api';
+import { ProblemDetailsModel, DocumentModel } from '@umbraco-cms/backend-api';
import type { UmbTreeRepository } from 'libs/repository/tree-repository.interface';
import { UmbDetailRepository } from '@umbraco-cms/repository';
-import type { DocumentDetails } from '@umbraco-cms/models';
import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/notification';
-type ItemDetailType = DocumentDetails;
+type ItemType = DocumentModel;
// Move to documentation / JSdoc
/* We need to create a new instance of the repository from within the element context. We want the notifications to be displayed in the right context. */
// element -> context -> repository -> (store) -> data source
// All methods should be async and return a promise. Some methods might return an observable as part of the promise response.
-export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailRepository {
+export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailRepository {
#init!: Promise;
#host: UmbControllerHostInterface;
@@ -25,8 +24,8 @@ export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailReposi
#treeSource: RepositoryTreeDataSource;
#treeStore?: UmbDocumentTreeStore;
- #detailDataSource: UmbDocumentDetailServerDataSource;
- #detailStore?: UmbDocumentDetailStore;
+ #detailDataSource: UmbDocumentServerDataSource;
+ #detailStore?: UmbDocumentStore;
#notificationService?: UmbNotificationService;
@@ -35,7 +34,7 @@ export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailReposi
// TODO: figure out how spin up get the correct data source
this.#treeSource = new DocumentTreeServerDataSource(this.#host);
- this.#detailDataSource = new UmbDocumentDetailServerDataSource(this.#host);
+ this.#detailDataSource = new UmbDocumentServerDataSource(this.#host);
this.#init = Promise.all([
new UmbContextConsumerController(this.#host, UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN, (instance) => {
@@ -124,7 +123,7 @@ export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailReposi
return this.#detailDataSource.createScaffold(parentKey);
}
- async requestDetails(key: string) {
+ async requestByKey(key: string) {
await this.#init;
// TODO: should we show a notification if the key is missing?
@@ -144,14 +143,14 @@ export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailReposi
// Could potentially be general methods:
- async createDetail(template: ItemDetailType) {
+ async createDetail(item: ItemType) {
await this.#init;
- if (!template || !template.key) {
- throw new Error('Template is missing');
+ if (!item || !item.key) {
+ throw new Error('Document is missing');
}
- const { error } = await this.#detailDataSource.insert(template);
+ const { error } = await this.#detailDataSource.insert(item);
if (!error) {
const notification = { data: { message: `Document created` } };
@@ -160,20 +159,20 @@ export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailReposi
// TODO: we currently don't use the detail store for anything.
// Consider to look up the data before fetching from the server
- this.#detailStore?.append(template);
+ this.#detailStore?.append(item);
// TODO: Update tree store with the new item? or ask tree to request the new item?
return { error };
}
- async saveDetail(document: ItemDetailType) {
+ async saveDetail(item: ItemType) {
await this.#init;
- if (!document || !document.key) {
- throw new Error('Template is missing');
+ if (!item || !item.key) {
+ throw new Error('Document is missing');
}
- const { error } = await this.#detailDataSource.update(document);
+ const { error } = await this.#detailDataSource.update(item);
if (!error) {
const notification = { data: { message: `Document saved` } };
@@ -182,10 +181,9 @@ export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailReposi
// 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 template is updated in the store while someone is editing it.
- this.#detailStore?.append(document);
- this.#treeStore?.updateItem(document.key, { name: document.name });
-
+ // Consider notify a workspace if a document is updated in the store while someone is editing it.
+ this.#detailStore?.append(item);
+ //this.#treeStore?.updateItem(item.key, { name: item.name });// Port data to tree store.
// TODO: would be nice to align the stores on methods/methodNames.
return { error };
@@ -209,7 +207,7 @@ export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailReposi
// 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 template is deleted from the store while someone is editing it.
+ // Consider notify a workspace if a document is deleted from the store while someone is editing it.
this.#detailStore?.remove([key]);
this.#treeStore?.removeItem(key);
// TODO: would be nice to align the stores on methods/methodNames.
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.store.ts
similarity index 62%
rename from src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.detail.store.ts
rename to src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.store.ts
index 189c4ddf38..274ef33130 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.detail.store.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.store.ts
@@ -1,4 +1,4 @@
-import type { DocumentDetails } from '@umbraco-cms/models';
+import { DocumentModel } from '@umbraco-cms/backend-api';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { ArrayState } from '@umbraco-cms/observable-api';
import { UmbStoreBase } from '@umbraco-cms/store';
@@ -10,9 +10,8 @@ import { UmbControllerHostInterface } from '@umbraco-cms/controller';
* @extends {UmbStoreBase}
* @description - Data Store for Template Details
*/
-export class UmbDocumentDetailStore extends UmbStoreBase {
-
- #data = new ArrayState([], (x) => x.key);
+export class UmbDocumentStore extends UmbStoreBase {
+ #data = new ArrayState([], (x) => x.key);
/**
* Creates an instance of UmbDocumentDetailStore.
@@ -20,7 +19,7 @@ export class UmbDocumentDetailStore extends UmbStoreBase {
* @memberof UmbDocumentDetailStore
*/
constructor(host: UmbControllerHostInterface) {
- super(host, UmbDocumentDetailStore.name);
+ super(host, UmbDocumentStore.name);
}
/**
@@ -28,20 +27,27 @@ export class UmbDocumentDetailStore extends UmbStoreBase {
* @param {DocumentDetails} document
* @memberof UmbDocumentDetailStore
*/
- append(document: DocumentDetails) {
+ append(document: DocumentModel) {
this.#data.append([document]);
}
+ /**
+ * Append a document to the store
+ * @param {DocumentModel} document
+ * @memberof UmbDocumentStore
+ */
+ byKey(key: DocumentModel['key']) {
+ return this.#data.getObservablePart((x) => x.find((y) => y.key === key));
+ }
+
/**
* Removes documents in the store with the given uniques
* @param {string[]} uniques
* @memberof UmbDocumentDetailStore
*/
- remove(uniques: string[]) {
+ remove(uniques: Array) {
this.#data.remove(uniques);
}
}
-export const UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken(
- UmbDocumentDetailStore.name
-);
+export const UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken(UmbDocumentStore.name);
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/sources/document.detail.server.data.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/sources/document.server.data.ts
similarity index 65%
rename from src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/sources/document.detail.server.data.ts
rename to src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/sources/document.server.data.ts
index ba6f8c32f6..41480a0d0f 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/sources/document.detail.server.data.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/sources/document.server.data.ts
@@ -1,22 +1,21 @@
import { RepositoryDetailDataSource } from '@umbraco-cms/repository';
-import { ProblemDetailsModel } from '@umbraco-cms/backend-api';
+import { DocumentResource, ProblemDetailsModel, DocumentModel } from '@umbraco-cms/backend-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
-import type { DataSourceResponse, DocumentDetails } from '@umbraco-cms/models';
/**
- * A data source for the Template detail that fetches data from the server
+ * A data source for the Document that fetches data from the server
* @export
- * @class UmbTemplateDetailServerDataSource
- * @implements {TemplateDetailDataSource}
+ * @class UmbDocumentServerDataSource
+ * @implements {RepositoryDetailDataSource}
*/
-export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSource {
+export class UmbDocumentServerDataSource implements RepositoryDetailDataSource {
#host: UmbControllerHostInterface;
/**
- * Creates an instance of UmbDocumentDetailServerDataSource.
+ * Creates an instance of UmbDocumentServerDataSource.
* @param {UmbControllerHostInterface} host
- * @memberof UmbDocumentDetailServerDataSource
+ * @memberof UmbDocumentServerDataSource
*/
constructor(host: UmbControllerHostInterface) {
this.#host = host;
@@ -26,7 +25,7 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
* Fetches a Document with the given key from the server
* @param {string} key
* @return {*}
- * @memberof UmbDocumentDetailServerDataSource
+ * @memberof UmbDocumentServerDataSource
*/
async get(key: string) {
if (!key) {
@@ -36,10 +35,9 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
return tryExecuteAndNotify(
this.#host,
- // TODO: use backend cli when available.
- fetch(`/umbraco/management/api/v1/document/details/${key}`)
- .then((res) => res.json())
- .then((res) => res[0] || undefined)
+ DocumentResource.getDocumentByKey({
+ key,
+ })
);
}
@@ -47,37 +45,13 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
* Creates a new Document scaffold
* @param {(string | null)} parentKey
* @return {*}
- * @memberof UmbDocumentDetailServerDataSource
+ * @memberof UmbDocumentServerDataSource
*/
async createScaffold(parentKey: string | null) {
- const data: DocumentDetails = {
- key: '',
- name: '',
- icon: '',
- type: '',
- hasChildren: false,
- parentKey: parentKey ?? '',
- isTrashed: false,
- properties: [
- {
- alias: '',
- label: '',
- description: '',
- dataTypeKey: '',
- },
- ],
- data: [
- {
- alias: '',
- value: '',
- },
- ],
- variants: [
- {
- name: '',
- },
- ],
- } as DocumentDetails;
+ const data: DocumentModel = {
+ properties: [],
+ variants: [],
+ };
return { data };
}
@@ -86,9 +60,9 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
* Inserts a new Document on the server
* @param {Document} document
* @return {*}
- * @memberof UmbDocumentDetailServerDataSource
+ * @memberof UmbDocumentServerDataSource
*/
- async insert(document: DocumentDetails) {
+ async insert(document: DocumentModel) {
if (!document.key) {
//const error: ProblemDetails = { title: 'Document key is missing' };
return Promise.reject();
@@ -104,7 +78,8 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
return Promise.reject();
}
//return tryExecuteAndNotify(this.#host, DocumentResource.postDocument(payload));
- return tryExecuteAndNotify(
+ // TODO: use resources when end point is ready:
+ return tryExecuteAndNotify(
this.#host,
fetch('/umbraco/management/api/v1/document/save', {
method: 'POST',
@@ -120,10 +95,10 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
* Updates a Document on the server
* @param {Document} Document
* @return {*}
- * @memberof UmbDocumentDetailServerDataSource
+ * @memberof UmbDocumentServerDataSource
*/
// TODO: Error mistake in this:
- async update(document: DocumentDetails) {
+ async update(document: DocumentModel) {
if (!document.key) {
const error: ProblemDetailsModel = { title: 'Document key is missing' };
return { error };
@@ -139,7 +114,8 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
return { error: myError };
}
- return tryExecuteAndNotify(
+ // TODO: use resources when end point is ready:
+ return tryExecuteAndNotify(
this.#host,
fetch('/umbraco/management/api/v1/document/save', {
method: 'POST',
@@ -155,7 +131,7 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
* Trash a Document on the server
* @param {Document} Document
* @return {*}
- * @memberof UmbDocumentDetailServerDataSource
+ * @memberof UmbDocumentServerDataSource
*/
async trash(key: string) {
if (!key) {
@@ -163,7 +139,8 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
return { error };
}
- return tryExecuteAndNotify(
+ // TODO: use resources when end point is ready:
+ return tryExecuteAndNotify(
this.#host,
fetch('/umbraco/management/api/v1/document/trash', {
method: 'POST',
@@ -176,10 +153,10 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
}
/**
- * Deletes a Template on the server
+ * Deletes a Document on the server
* @param {string} key
* @return {*}
- * @memberof UmbTemplateDetailServerDataSource
+ * @memberof UmbDocumentServerDataSource
*/
// TODO: Error mistake in this:
async delete(key: string) {
@@ -188,6 +165,7 @@ export class UmbDocumentDetailServerDataSource implements RepositoryDetailDataSo
return { error };
}
+ // TODO: use resources when end point is ready:
return tryExecuteAndNotify(
this.#host,
fetch('/umbraco/management/api/v1/document/trash', {
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts
index 14e05af6b0..9832192b15 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts
@@ -1,29 +1,103 @@
import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context';
import { UmbDocumentRepository } from '../repository/document.repository';
import type { UmbWorkspaceEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-entity-context.interface';
-import type { DocumentDetails } from '@umbraco-cms/models';
-import { appendToFrozenArray, ObjectState } from '@umbraco-cms/observable-api';
+import { UmbDocumentTypeRepository } from '../../document-types/repository/document-type.repository';
+import type { DocumentModel, DocumentTypeModel } from '@umbraco-cms/backend-api';
+import {
+ partialUpdateFrozenArray,
+ ObjectState,
+ ArrayState,
+ UmbObserverController,
+ appendToFrozenArray,
+} from '@umbraco-cms/observable-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
-// TODO: should this contex be called DocumentDraft instead of workspace? or should the draft be part of this?
+// TODO: should this context be called DocumentDraft instead of workspace? or should the draft be part of this?
-type EntityType = DocumentDetails;
+type EntityType = DocumentModel;
export class UmbDocumentWorkspaceContext
extends UmbWorkspaceContext
implements UmbWorkspaceEntityContextInterface
{
#isNew = false;
#host: UmbControllerHostInterface;
- #templateDetailRepo: UmbDocumentRepository;
+ #documentRepository: UmbDocumentRepository;
+ #documentTypeRepository: UmbDocumentTypeRepository;
+ //#dataTypeRepository: UmbDataTypeRepository;
#data = new ObjectState(undefined);
- data = this.#data.asObservable();
- name = this.#data.getObservablePart((data) => data?.name);
+ documentTypeKey = this.#data.getObservablePart((data) => data?.contentTypeKey);
+
+ #documentTypes = new ArrayState([], (x) => x.key);
+ documentTypes = this.#documentTypes.asObservable();
constructor(host: UmbControllerHostInterface) {
super(host);
this.#host = host;
- this.#templateDetailRepo = new UmbDocumentRepository(this.#host);
+ this.#documentRepository = new UmbDocumentRepository(this.#host);
+ this.#documentTypeRepository = new UmbDocumentTypeRepository(this.#host);
+ //this.#dataTypeRepository = new UmbDataTypeRepository(this.#host);
+
+ new UmbObserverController(this._host, this.documentTypeKey, (key) => this.loadDocumentType(key));
+ }
+
+ async load(entityKey: string) {
+ const { data } = await this.#documentRepository.requestByKey(entityKey);
+ if (data) {
+ this.#isNew = false;
+ this.#data.next(data);
+ }
+ }
+
+ async createScaffold(parentKey: string | null) {
+ const { data } = await this.#documentRepository.createDetailsScaffold(parentKey);
+ if (!data) return;
+ this.#isNew = true;
+ this.#data.next(data);
+ }
+
+ async loadDocumentType(key?: string) {
+ if (!key) return;
+
+ const { data } = await this.#documentTypeRepository.requestByKey(key);
+ if (!data) return;
+
+ // Load inherited and composed types:
+ await data?.compositions?.forEach(async (composition) => {
+ if (composition.key) {
+ this.loadDocumentType(composition.key);
+ }
+ });
+
+ new UmbObserverController(this._host, await this.#documentTypeRepository.byKey(key), (data) => {
+ if (data) {
+ this.#documentTypes.appendOne(data);
+ this.loadDataTypeOfDocumentType(data);
+ }
+ });
+ }
+
+ async loadDataTypeOfDocumentType(documentType?: DocumentTypeModel) {
+ if (!documentType) return;
+
+ // Load inherited and composed types:
+ await documentType?.properties?.forEach(async (property) => {
+ if (property.dataTypeKey) {
+ this.loadDataType(property.dataTypeKey);
+ }
+ });
+ }
+
+ async loadDataType(key?: string) {
+ if (!key) return;
+
+ //const { data } = await this.#dataTypeRepository.requestDetails(key);
+
+ /*new UmbObserverController(this._host, await this.#documentTypeRepository.byKey(key), (data) => {
+ if (data) {
+ this.#documentTypes.appendOne(data);
+ }
+ });*/
}
getData() {
@@ -44,8 +118,14 @@ export class UmbDocumentWorkspaceContext
return 'document';
}
- setName(name: string) {
- this.#data.update({ name });
+ setName(name: string, culture?: string | null, segment?: string | null) {
+ const variants = this.#data.getValue()?.variants || [];
+ const newVariants = partialUpdateFrozenArray(
+ variants,
+ { name },
+ (v) => v.culture == culture && v.segment == segment
+ );
+ this.#data.update({ variants: newVariants });
}
/*
getEntityType = this.#manager.getEntityType;
@@ -68,48 +148,43 @@ export class UmbDocumentWorkspaceContext
this.#draft.next(data)
})
}
+ */
- */
+ propertiesOf(culture: string | null, segment: string | null) {
+ return this.#data.getObservablePart((data) =>
+ data?.properties?.filter((p) => (culture === p.culture || null) && (segment === p.segment || null))
+ );
+ }
+
+ propertyStructure() {
+ // TODO: handle composition of document types.
+ return this.#documentTypes.getObservablePart((data) => data[0]?.properties);
+ }
setPropertyValue(alias: string, value: unknown) {
const entry = { alias: alias, value: value };
const currentData = this.#data.value;
if (currentData) {
- const newDataSet = appendToFrozenArray(currentData.data, entry, (x) => x.alias);
-
- this.#data.update({ data: newDataSet });
+ // TODO: make a partial update method for array of data, (idea/concept, use if this case is getting common)
+ const newDataSet = appendToFrozenArray(currentData.properties || [], entry, (x) => x.alias);
+ this.#data.update({ properties: newDataSet });
}
}
- async load(entityKey: string) {
- const { data } = await this.#templateDetailRepo.requestDetails(entityKey);
- if (data) {
- this.#isNew = false;
- this.#data.next(data);
- }
- }
-
- async createScaffold(parentKey: string | null) {
- const { data } = await this.#templateDetailRepo.createDetailsScaffold(parentKey);
- if (!data) return;
- this.#isNew = true;
- this.#data.next(data);
- }
-
async save() {
if (!this.#data.value) return;
if (this.#isNew) {
- await this.#templateDetailRepo.createDetail(this.#data.value);
+ await this.#documentRepository.createDetail(this.#data.value);
} else {
- await this.#templateDetailRepo.saveDetail(this.#data.value);
+ await this.#documentRepository.saveDetail(this.#data.value);
}
// If it went well, then its not new anymore?.
this.#isNew = false;
}
async delete(key: string) {
- await this.#templateDetailRepo.delete(key);
+ await this.#documentRepository.delete(key);
}
/*
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.stories.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.stories.ts
index 01847bd2a7..8475de493d 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.stories.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.stories.ts
@@ -1,8 +1,8 @@
import './document-workspace.element';
import { Meta, Story } from '@storybook/web-components';
import { html } from 'lit-html';
+import { data as documentNodes } from '../../../../core/mocks/data/document.data';
import type { UmbDocumentWorkspaceElement } from './document-workspace.element';
-import { data as documentNodes } from 'src/core/mocks/data/document.data';
export default {
title: 'Workspaces/Document',
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/manifests.ts
index 2666423faf..5ea6bbb231 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/manifests.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/manifests.ts
@@ -25,8 +25,7 @@ const workspaceViews: Array = [
type: 'workspaceView',
alias: 'Umb.WorkspaceView.Document.Edit',
name: 'Document Workspace Edit View',
- loader: () =>
- import('../../../shared/components/workspace/workspace-content/views/edit/workspace-view-content-edit.element'),
+ loader: () => import('./views/workspace-view-document-edit.element'),
weight: 200,
meta: {
workspaces: ['Umb.Workspace.Document'],
@@ -52,6 +51,8 @@ const workspaceViews: Array = [
];
const workspaceViewCollections: Array = [
+ /*
+ // TODO: Reenable this:
{
type: 'workspaceViewCollection',
alias: 'Umb.WorkspaceView.Document.Collection',
@@ -66,6 +67,7 @@ const workspaceViewCollections: Array = [
repositoryAlias: DOCUMENT_REPOSITORY_ALIAS,
},
},
+ */
];
const workspaceActions: Array = [
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/views/workspace-view-document-edit.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/views/workspace-view-document-edit.element.ts
new file mode 100644
index 0000000000..c5a1cba82a
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/views/workspace-view-document-edit.element.ts
@@ -0,0 +1,93 @@
+import { css, html } from 'lit';
+import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
+import { customElement, state } from 'lit/decorators.js';
+import { repeat } from 'lit/directives/repeat.js';
+import { UmbDocumentWorkspaceContext } from '../document-workspace.context';
+import { UmbLitElement } from '@umbraco-cms/element';
+import { DocumentPropertyModel, DocumentTypePropertyTypeModel } from '@umbraco-cms/backend-api';
+
+@customElement('umb-workspace-view-document-edit')
+export class UmbWorkspaceViewDocumentEditElement extends UmbLitElement {
+ static styles = [
+ UUITextStyles,
+ css`
+ :host {
+ display: block;
+ margin: var(--uui-size-layout-1);
+ }
+ `,
+ ];
+
+ @state()
+ _propertyData: DocumentPropertyModel[] = [];
+
+ @state()
+ _propertyStructures: DocumentTypePropertyTypeModel[] = [];
+
+ private _workspaceContext?: UmbDocumentWorkspaceContext;
+
+ constructor() {
+ super();
+
+ // TODO: Figure out how to get the magic string for the workspace context.
+ this.consumeContext('umbWorkspaceContext', (workspaceContext) => {
+ this._workspaceContext = workspaceContext;
+ this._observeContent();
+ });
+ }
+
+ private _observeContent() {
+ if (!this._workspaceContext) return;
+
+ /*
+ TODO: Property-Context: This observer gets all changes, We need to fix this. it should be simpler.
+ An idea to optimize this would be for this to only care about layout, meaning to property data should be watched here.
+ As the properties could handle their own data on their own?
+
+ Should use a Observable for example: this._workspaceContext.properties
+ */
+ this.observe(
+ this._workspaceContext.propertiesOf(null, null),
+ (properties) => {
+ this._propertyData = properties || [];
+ //this._data = content?.data || [];
+
+ /*
+ Maybe we should not give the value(Data), but the umb-content-property should get the context and observe its own data.
+ This would become a more specific Observer therefor better performance?.. Note to self: Debate with Mads how he sees this perspective.
+ */
+ },
+ 'observeWorkspaceContextData'
+ );
+ this.observe(
+ this._workspaceContext.propertyStructure(),
+ (propertyStructure) => {
+ this._propertyStructures = propertyStructure || [];
+ },
+ 'observeWorkspaceContextData'
+ );
+ }
+
+ render() {
+ return html`
+
+ ${repeat(
+ this._propertyStructures,
+ (property) => property.alias,
+ (property) =>
+ html` x.alias === property.alias)?.value}> `
+ )}
+
+ `;
+ }
+}
+
+export default UmbWorkspaceViewDocumentEditElement;
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'umb-workspace-view-document-edit': UmbWorkspaceViewDocumentEditElement;
+ }
+}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/media-type.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/media-type.detail.store.ts
index f27ba99859..bb952da590 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/media-type.detail.store.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/media-type.detail.store.ts
@@ -1,12 +1,12 @@
-import type { DataTypeDetails, MediaTypeDetails } from '@umbraco-cms/models';
+import type { MediaTypeDetails } from '@umbraco-cms/models';
import { UmbContextToken } from '@umbraco-cms/context-api';
import { ArrayState } from '@umbraco-cms/observable-api';
import { UmbEntityDetailStore, UmbStoreBase } from '@umbraco-cms/store';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
-
-export const UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbMediaTypeDetailStore');
-
+export const UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken(
+ 'UmbMediaTypeDetailStore'
+);
/**
* @export
@@ -15,25 +15,20 @@ export const UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken {
-
-
private _data = new ArrayState([], (x) => x.key);
-
constructor(host: UmbControllerHostInterface) {
super(host, UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString());
}
-
getScaffold(entityType: string, parentKey: string | null) {
- return {
- } as MediaTypeDetails;
+ return {} as MediaTypeDetails;
}
/**
* @description - Request a Data Type by key. The Data Type is added to the store and is returned as an Observable.
* @param {string} key
- * @return {*} {(Observable)}
+ * @return {*} {(Observable)}
* @memberof UmbMediaTypesStore
*/
getByKey(key: string) {
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.repository.ts
index dc5ae722c7..365e728589 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.repository.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.repository.ts
@@ -121,7 +121,7 @@ export class UmbMediaRepository implements UmbTreeRepository, UmbDetailRepositor
return this.#detailDataSource.createScaffold(parentKey);
}
- async requestDetails(key: string) {
+ async requestByKey(key: string) {
await this.#init;
// TODO: should we show a notification if the key is missing?
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/workspace/media-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/workspace/media-workspace.context.ts
index ca13c9b3b1..c287034bf1 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/workspace/media-workspace.context.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/workspace/media-workspace.context.ts
@@ -52,7 +52,7 @@ export class UmbMediaWorkspaceContext
}
async load(entityKey: string) {
- const { data } = await this.#detailRepository.requestDetails(entityKey);
+ const { data } = await this.#detailRepository.requestByKey(entityKey);
if (data) {
this.#isNew = false;
this.#data.next(data);
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.detail.store.ts
deleted file mode 100644
index 5c81233e1b..0000000000
--- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.detail.store.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-import type { DataTypeDetails } from '@umbraco-cms/models';
-import { UmbContextToken } from '@umbraco-cms/context-api';
-import { ArrayState } from '@umbraco-cms/observable-api';
-import { UmbEntityDetailStore, UmbStoreBase } from '@umbraco-cms/store';
-import { UmbControllerHostInterface } from '@umbraco-cms/controller';
-
-
-export const UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbDataTypeDetailStore');
-
-
-/**
- * @export
- * @class UmbDataTypeDetailStore
- * @extends {UmbStoreBase}
- * @description - Details Data Store for Data Types
- */
-export class UmbDataTypeDetailStore extends UmbStoreBase implements UmbEntityDetailStore {
-
-
- #data = new ArrayState([], (x) => x.key);
-
-
- constructor(host: UmbControllerHostInterface) {
- super(host, UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString());
- }
-
-
- getScaffold(entityType: string, parentKey: string | null) {
- return {
- key: '',
- name: '',
- icon: '',
- type: 'data-type',
- hasChildren: false,
- parentKey: '',
- propertyEditorModelAlias: '',
- propertyEditorUIAlias: '',
- data: [],
- } as DataTypeDetails;
- }
-
- /**
- * @description - Request a Data Type by key. The Data Type is added to the store and is returned as an Observable.
- * @param {string} key
- * @return {*} {(Observable)}
- * @memberof UmbDataTypesStore
- */
- getByKey(key: string) {
- // TODO: use backend cli when available.
- fetch(`/umbraco/backoffice/data-type/details/${key}`)
- .then((res) => res.json())
- .then((data) => {
- this.#data.append(data);
- });
-
-
- return this.#data.getObservablePart((documents) =>
- documents.find((document) => document.key === key)
- );
- }
-
- // TODO: make sure UI somehow can follow the status of this action.
- /**
- * @description - Save a Data Type.
- * @param {Array} dataTypes
- * @memberof UmbDataTypesStore
- * @return {*} {Promise}
- */
- save(data: DataTypeDetails[]) {
- // fetch from server and update store
- // TODO: use Fetcher API.
- let body: string;
-
- try {
- body = JSON.stringify(data);
- } catch (error) {
- console.error(error);
- return Promise.reject();
- }
-
- // TODO: use backend cli when available.
- return fetch('/umbraco/management/api/v1/data-type/save', {
- method: 'POST',
- body: body,
- headers: {
- 'Content-Type': 'application/json',
- },
- })
- .then((res) => res.json())
- .then((data: Array) => {
- this.#data.append(data);
- });
- }
-
- // TODO: How can we avoid having this in both stores?
- /**
- * @description - Delete a Data Type.
- * @param {string[]} keys
- * @memberof UmbDataTypesStore
- * @return {*} {Promise}
- */
- async delete(keys: string[]) {
- // TODO: use backend cli when available.
- await fetch('/umbraco/backoffice/data-type/delete', {
- method: 'POST',
- body: JSON.stringify(keys),
- headers: {
- 'Content-Type': 'application/json',
- },
- });
-
- this.#data.remove(keys);
- }
-}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.repository.ts
new file mode 100644
index 0000000000..0b93de9956
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.repository.ts
@@ -0,0 +1,222 @@
+import type { RepositoryTreeDataSource } from '../../../../../libs/repository/repository-tree-data-source.interface';
+import { UmbDataTypeTreeStore, UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN } from './data-type.tree.store';
+import { UmbDataTypeServerDataSource } from './sources/data-type.server.data';
+import { UmbDataTypeStore, UMB_DATA_TYPE_STORE_CONTEXT_TOKEN } from './data-type.store';
+import { DataTypeTreeServerDataSource } from './sources/data-type.tree.server.data';
+import { UmbControllerHostInterface } from '@umbraco-cms/controller';
+import { UmbContextConsumerController } from '@umbraco-cms/context-api';
+import { ProblemDetailsModel, DataTypeModel } from '@umbraco-cms/backend-api';
+import type { UmbTreeRepository } from 'libs/repository/tree-repository.interface';
+import { UmbDetailRepository } from '@umbraco-cms/repository';
+import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/notification';
+
+type ItemType = DataTypeModel;
+
+// Move to documentation / JSdoc
+/* We need to create a new instance of the repository from within the element context. We want the notifications to be displayed in the right context. */
+// element -> context -> repository -> (store) -> data source
+// All methods should be async and return a promise. Some methods might return an observable as part of the promise response.
+export class UmbDataTypeRepository implements UmbTreeRepository, UmbDetailRepository {
+ #init!: Promise;
+
+ #host: UmbControllerHostInterface;
+
+ #treeSource: RepositoryTreeDataSource;
+ #treeStore?: UmbDataTypeTreeStore;
+
+ #detailDataSource: UmbDataTypeServerDataSource;
+ #detailStore?: UmbDataTypeStore;
+
+ #notificationService?: UmbNotificationService;
+
+ constructor(host: UmbControllerHostInterface) {
+ this.#host = host;
+
+ // TODO: figure out how spin up get the correct data source
+ this.#treeSource = new DataTypeTreeServerDataSource(this.#host);
+ this.#detailDataSource = new UmbDataTypeServerDataSource(this.#host);
+
+ this.#init = Promise.all([
+ new UmbContextConsumerController(this.#host, UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN, (instance) => {
+ this.#treeStore = instance;
+ }),
+
+ new UmbContextConsumerController(this.#host, UMB_DATA_TYPE_STORE_CONTEXT_TOKEN, (instance) => {
+ this.#detailStore = instance;
+ }),
+
+ new UmbContextConsumerController(this.#host, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN, (instance) => {
+ this.#notificationService = instance;
+ }),
+ ]);
+ }
+
+ // TODO: Trash
+ // TODO: Move
+
+ async requestRootTreeItems() {
+ await this.#init;
+
+ const { data, error } = await this.#treeSource.getRootItems();
+
+ if (data) {
+ this.#treeStore?.appendItems(data.items);
+ }
+
+ return { data, error, asObservable: () => this.#treeStore!.rootItems };
+ }
+
+ async requestTreeItemsOf(parentKey: string | null) {
+ await this.#init;
+
+ if (!parentKey) {
+ const error: ProblemDetailsModel = { title: 'Parent key is missing' };
+ return { data: undefined, error };
+ }
+
+ const { data, error } = await this.#treeSource.getChildrenOf(parentKey);
+
+ if (data) {
+ this.#treeStore?.appendItems(data.items);
+ }
+
+ return { data, error, asObservable: () => this.#treeStore!.childrenOf(parentKey) };
+ }
+
+ async requestTreeItems(keys: Array) {
+ await this.#init;
+
+ if (!keys) {
+ const error: ProblemDetailsModel = { title: 'Keys are missing' };
+ return { data: undefined, error };
+ }
+
+ const { data, error } = await this.#treeSource.getItems(keys);
+
+ return { data, error, asObservable: () => this.#treeStore!.items(keys) };
+ }
+
+ async rootTreeItems() {
+ await this.#init;
+ return this.#treeStore!.rootItems;
+ }
+
+ async treeItemsOf(parentKey: string | null) {
+ await this.#init;
+ return this.#treeStore!.childrenOf(parentKey);
+ }
+
+ async treeItems(keys: Array) {
+ await this.#init;
+ return this.#treeStore!.items(keys);
+ }
+
+ // DETAILS:
+
+ async createDetailsScaffold(parentKey: string | null) {
+ await this.#init;
+
+ if (!parentKey) {
+ throw new Error('Parent key is missing');
+ }
+
+ return this.#detailDataSource.createScaffold(parentKey);
+ }
+
+ async requestByKey(key: string) {
+ await this.#init;
+
+ // TODO: should we show a notification if the key is missing?
+ // Investigate what is best for Acceptance testing, cause in that perspective a thrown error might be the best choice?
+ if (!key) {
+ const error: ProblemDetailsModel = { title: 'Key is missing' };
+ return { error };
+ }
+ const { data, error } = await this.#detailDataSource.get(key);
+
+ if (data) {
+ this.#detailStore?.append(data);
+ }
+
+ return { data, error };
+ }
+
+ async byKey(key: string) {
+ await this.#init;
+ return this.#detailStore!.byKey(key);
+ }
+
+ // Could potentially be general methods:
+
+ async createDetail(template: ItemType) {
+ await this.#init;
+
+ if (!template || !template.key) {
+ throw new Error('Template is missing');
+ }
+
+ const { error } = await this.#detailDataSource.insert(template);
+
+ if (!error) {
+ const notification = { data: { message: `Document created` } };
+ this.#notificationService?.peek('positive', notification);
+ }
+
+ // TODO: we currently don't use the detail store for anything.
+ // Consider to look up the data before fetching from the server
+ this.#detailStore?.append(template);
+ // TODO: Update tree store with the new item? or ask tree to request the new item?
+
+ return { error };
+ }
+
+ async saveDetail(item: ItemType) {
+ await this.#init;
+
+ if (!item || !item.key) {
+ throw new Error('Document-Type is missing');
+ }
+
+ const { error } = await this.#detailDataSource.update(item);
+
+ if (!error) {
+ const notification = { data: { message: `Document saved` } };
+ this.#notificationService?.peek('positive', notification);
+ }
+
+ // 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 template is updated in the store while someone is editing it.
+ this.#detailStore?.append(item);
+ this.#treeStore?.updateItem(item.key, { name: item.name });
+ // TODO: would be nice to align the stores on methods/methodNames.
+
+ return { error };
+ }
+
+ // General:
+
+ async delete(key: string) {
+ await this.#init;
+
+ if (!key) {
+ throw new Error('Document key is missing');
+ }
+
+ const { error } = await this.#detailDataSource.delete(key);
+
+ if (!error) {
+ const notification = { data: { message: `Document deleted` } };
+ this.#notificationService?.peek('positive', notification);
+ }
+
+ // 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 template is deleted from the store while someone is editing it.
+ this.#detailStore?.remove([key]);
+ this.#treeStore?.removeItem(key);
+ // TODO: would be nice to align the stores on methods/methodNames.
+
+ return { error };
+ }
+}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.store.ts
new file mode 100644
index 0000000000..4cd7c8dad1
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.store.ts
@@ -0,0 +1,53 @@
+import type { DataTypeModel } from '@umbraco-cms/backend-api';
+import { UmbContextToken } from '@umbraco-cms/context-api';
+import { ArrayState } from '@umbraco-cms/observable-api';
+import { UmbStoreBase } from '@umbraco-cms/store';
+import { UmbControllerHostInterface } from '@umbraco-cms/controller';
+
+/**
+ * @export
+ * @class UmbDataTypeStore
+ * @extends {UmbStoreBase}
+ * @description - Data Store for Template Details
+ */
+export class UmbDataTypeStore extends UmbStoreBase {
+ #data = new ArrayState([], (x) => x.key);
+
+ /**
+ * Creates an instance of UmbDataTypeStore.
+ * @param {UmbControllerHostInterface} host
+ * @memberof UmbDataTypeStore
+ */
+ constructor(host: UmbControllerHostInterface) {
+ super(host, UmbDataTypeStore.name);
+ }
+
+ /**
+ * Append a data-type to the store
+ * @param {DataTypeModel} dataType
+ * @memberof UmbDataTypeStore
+ */
+ append(dataType: DataTypeModel) {
+ this.#data.append([dataType]);
+ }
+
+ /**
+ * Append a data-type to the store
+ * @param {key} DataTypeModel key.
+ * @memberof UmbDataTypeStore
+ */
+ byKey(key: DataTypeModel['key']) {
+ return this.#data.getObservablePart((x) => x.find((y) => y.key === key));
+ }
+
+ /**
+ * Removes data-types in the store with the given uniques
+ * @param {string[]} uniques
+ * @memberof UmbDataTypeStore
+ */
+ remove(uniques: Array) {
+ this.#data.remove(uniques);
+ }
+}
+
+export const UMB_DATA_TYPE_STORE_CONTEXT_TOKEN = new UmbContextToken(UmbDataTypeStore.name);
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.tree.store.ts
new file mode 100644
index 0000000000..3f1a440f1e
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.tree.store.ts
@@ -0,0 +1,93 @@
+import { EntityTreeItemModel } from '@umbraco-cms/backend-api';
+import { UmbContextToken } from '@umbraco-cms/context-api';
+import { ArrayState } from '@umbraco-cms/observable-api';
+import { UmbStoreBase } from '@umbraco-cms/store';
+import { UmbControllerHostInterface } from '@umbraco-cms/controller';
+
+/**
+ * @export
+ * @class UmbDataTypeTreeStore
+ * @extends {UmbStoreBase}
+ * @description - Tree Data Store for Data-Types
+ */
+// TODO: consider if tree store could be turned into a general EntityTreeStore class?
+export class UmbDataTypeTreeStore extends UmbStoreBase {
+ #data = new ArrayState([], (x) => x.key);
+
+ /**
+ * Creates an instance of UmbDataTypeTreeStore.
+ * @param {UmbControllerHostInterface} host
+ * @memberof UmbDataTypeTreeStore
+ */
+ constructor(host: UmbControllerHostInterface) {
+ super(host, UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN.toString());
+ }
+
+ /**
+ * Appends items to the store
+ * @param {Array} items
+ * @memberof UmbDataTypeTreeStore
+ */
+ appendItems(items: Array) {
+ this.#data.append(items);
+ }
+
+ /**
+ * Updates an item in the store
+ * @param {string} key
+ * @param {Partial} data
+ * @memberof UmbDataTypeTreeStore
+ */
+ updateItem(key: string, data: Partial) {
+ const entries = this.#data.getValue();
+ const entry = entries.find((entry) => entry.key === key);
+
+ if (entry) {
+ this.#data.appendOne({ ...entry, ...data });
+ }
+ }
+
+ /**
+ * Removes an item from the store
+ * @param {string} key
+ * @memberof UmbDataTypeTreeStore
+ */
+ removeItem(key: string) {
+ const entries = this.#data.getValue();
+ const entry = entries.find((entry) => entry.key === key);
+
+ if (entry) {
+ this.#data.remove([key]);
+ }
+ }
+
+ /**
+ * An observable to observe the root items
+ * @memberof UmbDataTypeTreeStore
+ */
+ rootItems = this.#data.getObservablePart((items) => items.filter((item) => item.parentKey === null));
+
+ /**
+ * Returns an observable to observe the children of a given parent
+ * @param {(string | null)} parentKey
+ * @return {*}
+ * @memberof UmbDataTypeTreeStore
+ */
+ childrenOf(parentKey: string | null) {
+ return this.#data.getObservablePart((items) => items.filter((item) => item.parentKey === parentKey));
+ }
+
+ /**
+ * Returns an observable to observe the items with the given keys
+ * @param {Array} keys
+ * @return {*}
+ * @memberof UmbDataTypeTreeStore
+ */
+ items(keys: Array) {
+ return this.#data.getObservablePart((items) => items.filter((item) => keys.includes(item.key ?? '')));
+ }
+}
+
+export const UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken(
+ UmbDataTypeTreeStore.name
+);
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/sources/data-type.server.data.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/sources/data-type.server.data.ts
new file mode 100644
index 0000000000..ef2a78e574
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/sources/data-type.server.data.ts
@@ -0,0 +1,152 @@
+import { RepositoryDetailDataSource } from '@umbraco-cms/repository';
+import {
+ ProblemDetailsModel,
+ DataTypeResource,
+ DataTypeModel,
+ DataTypeCreateModel,
+ DataTypeUpdateModel,
+} from '@umbraco-cms/backend-api';
+import { UmbControllerHostInterface } from '@umbraco-cms/controller';
+import { tryExecuteAndNotify } from '@umbraco-cms/resources';
+
+/**
+ * A data source for the Data Type that fetches data from the server
+ * @export
+ * @class UmbDataTypeServerDataSource
+ * @implements {RepositoryDetailDataSource}
+ */
+export class UmbDataTypeServerDataSource implements RepositoryDetailDataSource {
+ #host: UmbControllerHostInterface;
+
+ /**
+ * Creates an instance of UmbDataTypeServerDataSource.
+ * @param {UmbControllerHostInterface} host
+ * @memberof UmbDataTypeServerDataSource
+ */
+ constructor(host: UmbControllerHostInterface) {
+ this.#host = host;
+ }
+
+ /**
+ * Fetches a Data Type with the given key from the server
+ * @param {string} key
+ * @return {*}
+ * @memberof UmbDataTypeServerDataSource
+ */
+ async get(key: string) {
+ if (!key) {
+ const error: ProblemDetailsModel = { title: 'Key is missing' };
+ return { error };
+ }
+
+ return tryExecuteAndNotify(
+ this.#host,
+ DataTypeResource.getDataTypeByKey({
+ key,
+ })
+ );
+ }
+
+ /**
+ * Creates a new Data Type scaffold
+ * @param {(string | null)} parentKey
+ * @return {*}
+ * @memberof UmbDataTypeServerDataSource
+ */
+ async createScaffold(parentKey: string | null) {
+ const data: DataTypeModel = {
+ parentKey: parentKey,
+ };
+
+ return { data };
+ }
+
+ /**
+ * Inserts a new Data Type on the server
+ * @param {Document} dataType
+ * @return {*}
+ * @memberof UmbDataTypeServerDataSource
+ */
+ async insert(dataType: DataTypeModel) {
+ if (!dataType.key) {
+ const error: ProblemDetailsModel = { title: 'DataType key is missing' };
+ return { error };
+ }
+ const requestBody: DataTypeCreateModel = { ...dataType };
+
+ // TODO: use resources when end point is ready:
+ return tryExecuteAndNotify(
+ this.#host,
+ DataTypeResource.postDataType({
+ requestBody,
+ })
+ );
+ }
+
+ /**
+ * Updates a DataType on the server
+ * @param {DataTypeModel} DataType
+ * @return {*}
+ * @memberof UmbDataTypeServerDataSource
+ */
+ // TODO: Error mistake in this:
+ async update(dataType: DataTypeModel) {
+ if (!dataType.key) {
+ const error: ProblemDetailsModel = { title: 'DataType key is missing' };
+ return { error };
+ }
+
+ const requestBody: DataTypeUpdateModel = { ...dataType };
+
+ // TODO: use resources when end point is ready:
+ return tryExecuteAndNotify(
+ this.#host,
+ DataTypeResource.putDataTypeByKey({
+ key: dataType.key,
+ requestBody,
+ })
+ );
+ }
+
+ /**
+ * Trash a Document on the server
+ * @param {Document} Document
+ * @return {*}
+ * @memberof UmbDataTypeServerDataSource
+ */
+ async trash(key: string) {
+ if (!key) {
+ const error: ProblemDetailsModel = { title: 'DataType key is missing' };
+ return { error };
+ }
+
+ // TODO: use resources when end point is ready:
+ return tryExecuteAndNotify(
+ this.#host,
+ DataTypeResource.deleteDataTypeByKey({
+ key,
+ })
+ );
+ }
+
+ /**
+ * Deletes a Data Type on the server
+ * @param {string} key
+ * @return {*}
+ * @memberof UmbDataTypeServerDataSource
+ */
+ async delete(key: string) {
+ if (!key) {
+ const error: ProblemDetailsModel = { title: 'DataType key is missing' };
+ return { error };
+ }
+
+ // TODO: use resources when end point is ready:
+ return tryExecuteAndNotify(
+ this.#host,
+ DataTypeResource.deleteDataTypeByKey({
+ key,
+ })
+ );
+ }
+}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/sources/data-type.tree.server.data.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/sources/data-type.tree.server.data.ts
new file mode 100644
index 0000000000..1ddaf7e517
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/sources/data-type.tree.server.data.ts
@@ -0,0 +1,105 @@
+import type { RepositoryTreeDataSource } from '../../../../../../libs/repository/repository-tree-data-source.interface';
+import { ProblemDetailsModel, DataTypeResource } from '@umbraco-cms/backend-api';
+import { UmbControllerHostInterface } from '@umbraco-cms/controller';
+import { tryExecuteAndNotify } from '@umbraco-cms/resources';
+
+/**
+ * A data source for the Document tree that fetches data from the server
+ * @export
+ * @class DocumentTreeServerDataSource
+ * @implements {DocumentTreeDataSource}
+ */
+export class DataTypeTreeServerDataSource implements RepositoryTreeDataSource {
+ #host: UmbControllerHostInterface;
+
+ // TODO: how do we handle trashed items?
+ async trashItems(keys: Array) {
+ if (!keys) {
+ const error: ProblemDetailsModel = { title: 'DataType keys is missing' };
+ return { error };
+ }
+
+ // TODO: use resources when end point is ready:
+ /*
+ return tryExecuteAndNotify(
+ this.#host,
+ DataTypeResource.deleteDataTypeByKey({
+ key: keys,
+ })
+ );
+ */
+ return Promise.resolve({ error: null, data: null });
+ }
+
+ async moveItems(keys: Array, destination: string) {
+ // TODO: use backend cli when available.
+ return tryExecuteAndNotify(
+ this.#host,
+ fetch('/umbraco/management/api/v1/data-type/move', {
+ method: 'POST',
+ body: JSON.stringify({ keys, destination }),
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ })
+ );
+ }
+
+ /**
+ * Creates an instance of DocumentTreeServerDataSource.
+ * @param {UmbControllerHostInterface} host
+ * @memberof DocumentTreeServerDataSource
+ */
+ constructor(host: UmbControllerHostInterface) {
+ this.#host = host;
+ }
+
+ /**
+ * Fetches the root items for the tree from the server
+ * @return {*}
+ * @memberof DocumentTreeServerDataSource
+ */
+ async getRootItems() {
+ return tryExecuteAndNotify(this.#host, DataTypeResource.getTreeDataTypeRoot({}));
+ }
+
+ /**
+ * Fetches the children of a given parent key from the server
+ * @param {(string | null)} parentKey
+ * @return {*}
+ * @memberof DocumentTreeServerDataSource
+ */
+ async getChildrenOf(parentKey: string | null) {
+ if (!parentKey) {
+ const error: ProblemDetailsModel = { title: 'Parent key is missing' };
+ return { error };
+ }
+
+ return tryExecuteAndNotify(
+ this.#host,
+ DataTypeResource.getTreeDataTypeChildren({
+ parentKey,
+ })
+ );
+ }
+
+ /**
+ * Fetches the items for the given keys from the server
+ * @param {Array} keys
+ * @return {*}
+ * @memberof DocumentTreeServerDataSource
+ */
+ async getItems(keys: Array) {
+ if (keys) {
+ const error: ProblemDetailsModel = { title: 'Keys are missing' };
+ return { error };
+ }
+
+ return tryExecuteAndNotify(
+ this.#host,
+ DataTypeResource.getTreeDataTypeItem({
+ key: keys,
+ })
+ );
+ }
+}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/tree/actions/delete/action-data-type-delete.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/tree/actions/delete/action-data-type-delete.element.ts
index 3d80fb6313..cfbf8ffcc5 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/tree/actions/delete/action-data-type-delete.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/tree/actions/delete/action-data-type-delete.element.ts
@@ -2,8 +2,6 @@ import { UUITextStyles } from '@umbraco-ui/uui-css';
import { css, html } from 'lit';
import { customElement } from 'lit/decorators.js';
import { UmbModalService, UMB_MODAL_SERVICE_CONTEXT_TOKEN } from '../../../../../../core/modal';
-import { UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN } from '../../../data-type.detail.store';
-import type { UmbDataTypeDetailStore } from '../../../data-type.detail.store';
import UmbTreeItemActionElement from '../../../../../shared/components/tree/action/tree-item-action.element';
@customElement('umb-tree-action-data-type-delete')
@@ -11,7 +9,7 @@ export default class UmbTreeActionDataTypeDeleteElement extends UmbTreeItemActio
static styles = [UUITextStyles, css``];
private _modalService?: UmbModalService;
- private _dataTypeStore?: UmbDataTypeDetailStore;
+ //private _dataTypeStore?: UmbDataTypeStore;
connectedCallback(): void {
super.connectedCallback();
@@ -19,10 +17,6 @@ export default class UmbTreeActionDataTypeDeleteElement extends UmbTreeItemActio
this.consumeContext(UMB_MODAL_SERVICE_CONTEXT_TOKEN, (modalService) => {
this._modalService = modalService;
});
-
- this.consumeContext(UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN, (dataTypeStore) => {
- this._dataTypeStore = dataTypeStore;
- });
}
private _handleLabelClick() {
@@ -34,10 +28,13 @@ export default class UmbTreeActionDataTypeDeleteElement extends UmbTreeItemActio
});
modalHandler?.onClose().then(({ confirmed }: any) => {
+ //TODO: Generally no one should talk to stores directly.
+ /*
if (confirmed && this._treeContextMenuService && this._dataTypeStore && this._activeTreeItem) {
this._dataTypeStore?.delete([this._activeTreeItem.key]);
this._treeContextMenuService.close();
}
+ */
});
}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.context.ts
index fe4c51aabd..af0aaf7d8d 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.context.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.context.ts
@@ -1,51 +1,95 @@
import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context';
import { UmbWorkspaceEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-entity-context.interface';
-import { UmbEntityWorkspaceManager } from '../../../shared/components/workspace/workspace-context/entity-manager-controller';
-import { UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN } from '../data-type.detail.store';
-import type { DataTypeDetails } from '@umbraco-cms/models';
-import { appendToFrozenArray } from '@umbraco-cms/observable-api';
+import { UmbDataTypeRepository } from '../repository/data-type.repository';
+import type { DataTypeModel } from '@umbraco-cms/backend-api';
+import { appendToFrozenArray, ObjectState } from '@umbraco-cms/observable-api';
+import { UmbControllerHostInterface } from '@umbraco-cms/controller';
-export class UmbWorkspaceDataTypeContext extends UmbWorkspaceContext implements UmbWorkspaceEntityContextInterface {
+type EntityType = DataTypeModel;
- #manager = new UmbEntityWorkspaceManager(this._host, 'data-type', UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN);
+export class UmbWorkspaceDataTypeContext
+ extends UmbWorkspaceContext
+ implements UmbWorkspaceEntityContextInterface
+{
+ #isNew = false;
+ #host: UmbControllerHostInterface;
+ #dataTypeRepository: UmbDataTypeRepository;
+ #data = new ObjectState(undefined);
+ data = this.#data.asObservable();
+ name = this.#data.getObservablePart((data) => data?.name);
+ key = this.#data.getObservablePart((data) => data?.key);
-
-
-
- public readonly data = this.#manager.state.asObservable();
- public readonly name = this.#manager.state.getObservablePart((state) => state?.name);
-
- setName(name: string) {
- this.#manager.state.update({name: name});
+ constructor(host: UmbControllerHostInterface) {
+ super(host);
+ this.#host = host;
+ this.#dataTypeRepository = new UmbDataTypeRepository(this.#host);
}
- setPropertyEditorModelAlias(alias?: string) {
- this.#manager.state.update({propertyEditorModelAlias: alias});
- }
- setPropertyEditorUIAlias(alias?: string) {
- this.#manager.state.update({propertyEditorUIAlias: alias});
- }
- getEntityType = this.#manager.getEntityType;
- getUnique = this.#manager.getEntityKey;
- getEntityKey = this.#manager.getEntityKey;
- getStore = this.#manager.getStore;
- getData = this.#manager.getData;
- load = this.#manager.load;
- create = this.#manager.create;
- save = this.#manager.save;
- destroy = this.#manager.destroy;
-
- // This could eventually be moved out as well?
- setPropertyValue(alias: string, value: unknown) {
-
- const entry = {alias: alias, value: value};
-
- const currentData = this.#manager.getData();
- if (currentData) {
- const newDataSet = appendToFrozenArray(currentData.data, entry, x => x.alias);
-
- this.#manager.state.update({data: newDataSet});
+ async load(entityKey: string) {
+ const { data } = await this.#dataTypeRepository.requestByKey(entityKey);
+ if (data) {
+ this.#isNew = false;
+ this.#data.next(data);
}
}
+
+ async createScaffold(parentKey: string | null) {
+ const { data } = await this.#dataTypeRepository.createDetailsScaffold(parentKey);
+ if (!data) return;
+ this.#isNew = true;
+ this.#data.next(data);
+ }
+
+ getData() {
+ return this.#data.getValue();
+ }
+ getEntityKey() {
+ return this.getData()?.key || '';
+ }
+ getEntityType() {
+ return 'data-type';
+ }
+
+ setName(name: string) {
+ this.#data.update({ name });
+ }
+
+ setPropertyEditorAlias(alias?: string) {
+ this.#data.update({ propertyEditorAlias: alias });
+ }
+ setPropertyEditorUiAlias(alias?: string) {
+ this.#data.update({ propertyEditorUiAlias: alias });
+ }
+
+ // TODO: its not called a property in the model, but we do consider this way in our front-end
+ setPropertyValue(alias: string, value: unknown) {
+ const entry = { alias: alias, value: value };
+
+ const currentData = this.#data.value;
+ if (currentData) {
+ // TODO: make a partial update method for array of data, (idea/concept, use if this case is getting common)
+ const newDataSet = appendToFrozenArray(currentData.data || [], entry, (x) => x.alias);
+ this.#data.update({ data: newDataSet });
+ }
+ }
+
+ async save() {
+ if (!this.#data.value) return;
+ if (this.#isNew) {
+ await this.#dataTypeRepository.createDetail(this.#data.value);
+ } else {
+ await this.#dataTypeRepository.saveDetail(this.#data.value);
+ }
+ // If it went well, then its not new anymore?.
+ this.#isNew = false;
+ }
+
+ async delete(key: string) {
+ await this.#dataTypeRepository.delete(key);
+ }
+
+ public destroy(): void {
+ this.#data.complete();
+ }
}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.element.ts
index fb2461887b..35db70aa92 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.element.ts
@@ -33,10 +33,11 @@ export class UmbDataTypeWorkspaceElement extends UmbLitElement {
public load(value: string) {
this._workspaceContext?.load(value);
+ //this._unique = entityKey;
}
public create(parentKey: string | null) {
- this._workspaceContext?.create(parentKey);
+ this._workspaceContext.createScaffold(parentKey);
}
@state()
@@ -45,9 +46,9 @@ export class UmbDataTypeWorkspaceElement extends UmbLitElement {
constructor() {
super();
this.provideContext('umbWorkspaceContext', this._workspaceContext);
- this.observe(this._workspaceContext.data.pipe(distinctUntilChanged()), (dataType) => {
- if (dataType && dataType.name !== this._dataTypeName) {
- this._dataTypeName = dataType.name ?? '';
+ this.observe(this._workspaceContext.name, (dataTypeName) => {
+ if (dataTypeName !== this._dataTypeName) {
+ this._dataTypeName = dataTypeName ?? '';
}
});
}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/views/edit/data-type-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/views/edit/data-type-workspace-view-edit.element.ts
index fc0d05b2bf..c90f5d2b34 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/views/edit/data-type-workspace-view-edit.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/views/edit/data-type-workspace-view-edit.element.ts
@@ -4,7 +4,7 @@ import { customElement, state } from 'lit/decorators.js';
import { UmbModalService, UMB_MODAL_SERVICE_CONTEXT_TOKEN } from '../../../../../../core/modal';
import { UmbWorkspaceDataTypeContext } from '../../data-type-workspace.context';
import { UmbLitElement } from '@umbraco-cms/element';
-import type { DataTypeDetails } from '@umbraco-cms/models';
+import type { DataTypeModel } from '@umbraco-cms/backend-api';
import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api';
import '../../../../../shared/property-editors/shared/property-editor-config/property-editor-config.element';
@@ -23,7 +23,7 @@ export class UmbDataTypeWorkspaceViewEditElement extends UmbLitElement {
];
@state()
- _dataType?: DataTypeDetails;
+ _dataType?: DataTypeModel;
@state()
private _propertyEditorUIIcon = '';
@@ -32,10 +32,10 @@ export class UmbDataTypeWorkspaceViewEditElement extends UmbLitElement {
private _propertyEditorUIName = '';
@state()
- private _propertyEditorUIAlias = '';
+ private _propertyEditorUiAlias = '';
@state()
- private _propertyEditorModelAlias = '';
+ private _propertyEditorAlias = '';
@state()
private _data: Array = [];
@@ -66,33 +66,33 @@ export class UmbDataTypeWorkspaceViewEditElement extends UmbLitElement {
if (!dataType) return;
// TODO: handle if model is not of the type wanted.
- this._dataType = dataType as DataTypeDetails;
+ this._dataType = dataType;
- if (this._dataType.propertyEditorUIAlias !== this._propertyEditorUIAlias) {
- this._observePropertyEditorUI(this._dataType.propertyEditorUIAlias || undefined);
+ if (this._dataType.propertyEditorUiAlias !== this._propertyEditorUiAlias) {
+ this._observePropertyEditorUI(this._dataType.propertyEditorUiAlias || undefined);
}
- if (this._dataType.data !== this._data) {
+ if (this._dataType.data && this._dataType.data !== this._data) {
this._data = this._dataType.data;
}
});
}
- private _observePropertyEditorUI(propertyEditorUIAlias?: string) {
- if (!propertyEditorUIAlias) return;
+ private _observePropertyEditorUI(propertyEditorUiAlias?: string) {
+ if (!propertyEditorUiAlias) return;
this.observe(
- umbExtensionsRegistry.getByTypeAndAlias('propertyEditorUI', propertyEditorUIAlias),
+ umbExtensionsRegistry.getByTypeAndAlias('propertyEditorUI', propertyEditorUiAlias),
(propertyEditorUI) => {
// TODO: show error. We have stored a PropertyEditorUIAlias and can't find the PropertyEditorUI in the registry.
if (!propertyEditorUI) return;
this._propertyEditorUIName = propertyEditorUI?.meta.label ?? propertyEditorUI?.name ?? '';
- this._propertyEditorUIAlias = propertyEditorUI?.alias ?? '';
+ this._propertyEditorUiAlias = propertyEditorUI?.alias ?? '';
this._propertyEditorUIIcon = propertyEditorUI?.meta.icon ?? '';
- this._propertyEditorModelAlias = propertyEditorUI?.meta.propertyEditorModel ?? '';
+ this._propertyEditorAlias = propertyEditorUI?.meta.propertyEditorModel ?? '';
- this._workspaceContext?.setPropertyEditorModelAlias(this._propertyEditorModelAlias);
+ this._workspaceContext?.setPropertyEditorAlias(this._propertyEditorAlias);
}
);
}
@@ -101,7 +101,7 @@ export class UmbDataTypeWorkspaceViewEditElement extends UmbLitElement {
if (!this._dataType) return;
const modalHandler = this._modalService?.propertyEditorUIPicker({
- selection: this._propertyEditorUIAlias ? [this._propertyEditorUIAlias] : [],
+ selection: this._propertyEditorUiAlias ? [this._propertyEditorUiAlias] : [],
});
modalHandler?.onClose().then(({ selection } = {}) => {
@@ -110,10 +110,10 @@ export class UmbDataTypeWorkspaceViewEditElement extends UmbLitElement {
});
}
- private _selectPropertyEditorUI(propertyEditorUIAlias: string | undefined) {
- if (!this._dataType || this._dataType.propertyEditorUIAlias === propertyEditorUIAlias) return;
- this._workspaceContext?.setPropertyEditorUIAlias(propertyEditorUIAlias);
- this._observePropertyEditorUI(propertyEditorUIAlias);
+ private _selectPropertyEditorUI(propertyEditorUiAlias: string | undefined) {
+ if (!this._dataType || this._dataType.propertyEditorUiAlias === propertyEditorUiAlias) return;
+ this._workspaceContext?.setPropertyEditorUiAlias(propertyEditorUiAlias);
+ this._observePropertyEditorUI(propertyEditorUiAlias);
}
render() {
@@ -126,14 +126,14 @@ export class UmbDataTypeWorkspaceViewEditElement extends UmbLitElement {
private _renderPropertyEditorUI() {
return html`
- ${this._propertyEditorUIAlias
+ ${this._propertyEditorUiAlias
? html`
@@ -155,11 +155,11 @@ export class UmbDataTypeWorkspaceViewEditElement extends UmbLitElement {
private _renderConfig() {
return html`
- ${this._propertyEditorModelAlias && this._propertyEditorUIAlias
+ ${this._propertyEditorAlias && this._propertyEditorUiAlias
? html`
`
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/views/info/workspace-view-data-type-info.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/views/info/workspace-view-data-type-info.element.ts
index fa4ecf1bf8..d35877f8be 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/views/info/workspace-view-data-type-info.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/views/info/workspace-view-data-type-info.element.ts
@@ -1,17 +1,17 @@
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { css, html } from 'lit';
import { customElement, state } from 'lit/decorators.js';
-import { distinctUntilChanged } from 'rxjs';
import { UmbWorkspaceDataTypeContext } from '../../data-type-workspace.context';
-import type { DataTypeDetails } from '@umbraco-cms/models';
+
import { UmbLitElement } from '@umbraco-cms/element';
+import { DataTypeModel } from '@umbraco-cms/backend-api';
@customElement('umb-workspace-view-data-type-info')
export class UmbWorkspaceViewDataTypeInfoElement extends UmbLitElement {
static styles = [UUITextStyles, css``];
@state()
- _dataType?: DataTypeDetails;
+ _dataType?: DataTypeModel;
private _workspaceContext?: UmbWorkspaceDataTypeContext;
@@ -28,12 +28,9 @@ export class UmbWorkspaceViewDataTypeInfoElement extends UmbLitElement {
private _observeDataType() {
if (!this._workspaceContext) return;
- this.observe(this._workspaceContext.data.pipe(distinctUntilChanged()), (dataType) => {
+ this.observe(this._workspaceContext.data, (dataType) => {
if (!dataType) return;
-
- // TODO: handle if model is not of the type wanted.
- // TODO: Make method to identify wether data is of type DataTypeDetails
- this._dataType = dataType as DataTypeDetails;
+ this._dataType = dataType;
});
}
@@ -48,11 +45,11 @@ export class UmbWorkspaceViewDataTypeInfoElement extends UmbLitElement {