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 {
${this._dataType?.key}
-
${this._dataType?.propertyEditorModelAlias}
+
${this._dataType?.propertyEditorAlias}
-
${this._dataType?.propertyEditorUIAlias}
+
${this._dataType?.propertyEditorUiAlias}
`; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/language.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/language.store.ts index ee941e85fd..ffc3f178ba 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/language.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/language.store.ts @@ -73,10 +73,11 @@ export class UmbLanguageStore extends UmbStoreBase { } async delete(isoCodes: Array) { + // TODO: revisit this. It looks a bit weird with the nested tryExecuteAndNotify const queue = isoCodes.map((isoCode) => tryExecuteAndNotify( this._host, - LanguageResource.deleteLanguageByIsoCode({ isoCode }).then(() => isoCode) + tryExecuteAndNotify(this._host, LanguageResource.deleteLanguageByIsoCode({ isoCode })).then(() => isoCode) ) ); const results = await Promise.all(queue); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/content-property/content-property.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/content-property/content-property.element.ts index 744d5febe0..efb343d311 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/content-property/content-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/content-property/content-property.element.ts @@ -2,11 +2,8 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html } from 'lit'; import { ifDefined } from 'lit-html/directives/if-defined.js'; import { customElement, property, state } from 'lit/decorators.js'; - -import { UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN } from '../../../settings/data-types/data-type.detail.store'; -import type { UmbDataTypeDetailStore } from '../../../settings/data-types/data-type.detail.store'; -import type { ContentProperty, DataTypeDetails, DataTypePropertyData } from '@umbraco-cms/models'; - +import { UmbDataTypeRepository } from '../../../settings/data-types/repository/data-type.repository'; +import type { DataTypeModel, DataTypePropertyModel, DocumentTypePropertyTypeModel } from '@umbraco-cms/backend-api'; import '../workspace-property/workspace-property.element'; import { UmbLitElement } from '@umbraco-cms/element'; import { UmbObserverController } from '@umbraco-cms/observable-api'; @@ -23,12 +20,13 @@ export class UmbContentPropertyElement extends UmbLitElement { ]; // TODO: Consider if we just need to get the DataType Key?.. - private _property?: ContentProperty; + // TODO: consider if we should make a base type of the DocumentTypePropertyType, which could become the ContentProperty. A shared common type for all properties. + private _property?: DocumentTypePropertyTypeModel; @property({ type: Object, attribute: false }) - public get property(): ContentProperty | undefined { + public get property(): DocumentTypePropertyTypeModel | undefined { return this._property; } - public set property(value: ContentProperty | undefined) { + public set property(value: DocumentTypePropertyTypeModel | undefined) { const oldProperty = this._property; this._property = value; if (this._property?.dataTypeKey !== oldProperty?.dataTypeKey) { @@ -40,31 +38,22 @@ export class UmbContentPropertyElement extends UmbLitElement { value?: object | string; @state() - private _propertyEditorUIAlias?: string; + private _propertyEditorUiAlias?: string; @state() - private _dataTypeData: DataTypePropertyData[] = []; + private _dataTypeData: DataTypePropertyModel[] = []; - private _dataTypeStore?: UmbDataTypeDetailStore; - private _dataTypeObserver?: UmbObserverController; - - constructor() { - super(); - - this.consumeContext(UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN, (instance) => { - this._dataTypeStore = instance; - this._observeDataType(this._property?.dataTypeKey); - }); - } - - private _observeDataType(dataTypeKey?: string) { - if (!this._dataTypeStore) return; + private _dataTypeRepository: UmbDataTypeRepository = new UmbDataTypeRepository(this); + private _dataTypeObserver?: UmbObserverController; + private async _observeDataType(dataTypeKey?: string) { this._dataTypeObserver?.destroy(); if (dataTypeKey) { - this._dataTypeObserver = this.observe(this._dataTypeStore.getByKey(dataTypeKey), (dataType) => { + // We do not need to have await here, this is only to ensure that the data is loaded before we try to observe it, and thereby update the DOM with it. + await this._dataTypeRepository.requestByKey(dataTypeKey); + this._dataTypeObserver = this.observe(await this._dataTypeRepository.byKey(dataTypeKey), (dataType) => { this._dataTypeData = dataType?.data || []; - this._propertyEditorUIAlias = dataType?.propertyEditorUIAlias || undefined; + this._propertyEditorUiAlias = dataType?.propertyEditorUiAlias || undefined; }); } } @@ -72,9 +61,9 @@ export class UmbContentPropertyElement extends UmbLitElement { render() { return html``; } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/ref-property-editor-ui/ref-property-editor-ui.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/ref-property-editor-ui/ref-property-editor-ui.element.ts index ecc59ef74d..4a0f2a8d4f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/ref-property-editor-ui/ref-property-editor-ui.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/ref-property-editor-ui/ref-property-editor-ui.element.ts @@ -30,7 +30,7 @@ export class UmbRefPropertyEditorUIElement extends UUIRefNodeElement { * @default '' */ @property({ type: String, attribute: 'property-editor-model-alias' }) - propertyEditorModelAlias = ''; + propertyEditorAlias = ''; protected renderDetail() { const details: string[] = []; @@ -39,8 +39,8 @@ export class UmbRefPropertyEditorUIElement extends UUIRefNodeElement { details.push(this.alias); } - if (this.propertyEditorModelAlias !== '') { - details.push(this.propertyEditorModelAlias); + if (this.propertyEditorAlias !== '') { + details.push(this.propertyEditorAlias); } else { details.push('Property Editor Missing'); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/variant-selector/variant-selector.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/variant-selector/variant-selector.element.ts index 080c12702a..b79c22155f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/variant-selector/variant-selector.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/variant-selector/variant-selector.element.ts @@ -50,23 +50,23 @@ export class UmbVariantSelectorElement extends UmbLitElement { super(); // TODO: Figure out how to get the magic string for the workspace context. - this.consumeContext>( - 'umbWorkspaceContext', - (instance) => { - this._workspaceContext = instance; - this._observeWorkspace(); - } - ); + this.consumeContext>('umbWorkspaceContext', (instance) => { + this._workspaceContext = instance; + this._observeWorkspace(); + }); } private async _observeWorkspace() { if (!this._workspaceContext) return; + /* + // TODO: update this with nre repository and document types. this.observe(this._workspaceContext.data, (data) => { if(data) { this._content = data; } }); + */ } // TODO. find a way where we don't have to do this for all workspaces. diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts index 4a8986eac6..2a8f245f37 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts @@ -1,5 +1,5 @@ import { UmbWorkspaceEntityContextInterface } from '../workspace/workspace-context/workspace-entity-context.interface'; -import type { DataTypeDetails } from '@umbraco-cms/models'; +import type { DataTypeModel } from '@umbraco-cms/backend-api'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; import { ObjectState } from '@umbraco-cms/observable-api'; import { UmbContextConsumerController, UmbContextProviderController } from '@umbraco-cms/context-api'; @@ -10,7 +10,7 @@ export type WorkspacePropertyData = { label?: string; description?: string; value?: ValueType | null; - config?: DataTypeDetails['data']; // This could potentially then come from hardcoded JS object and not the DataType store. + config?: DataTypeModel['data']; // This could potentially then come from hardcoded JS object and not the DataType store. }; export class UmbWorkspacePropertyContext { @@ -28,9 +28,13 @@ export class UmbWorkspacePropertyContext { constructor(host: UmbControllerHostInterface) { // TODO: Figure out how to get the magic string in a better way. - new UmbContextConsumerController(host, 'umbWorkspaceContext', (workspaceContext) => { - this._workspaceContext = workspaceContext; - }); + new UmbContextConsumerController( + host, + 'umbWorkspaceContext', + (workspaceContext) => { + this._workspaceContext = workspaceContext; + } + ); this._providerController = new UmbContextProviderController(host, 'umbPropertyContext', this); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts index 7e4562b0e7..877c4874bf 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts @@ -3,13 +3,14 @@ import { css, html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { ifDefined } from 'lit-html/directives/if-defined.js'; import { UmbWorkspacePropertyContext } from './workspace-property.context'; -import { createExtensionElement , umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; -import type { DataTypePropertyData, ManifestPropertyEditorUI, ManifestTypes } from '@umbraco-cms/models'; +import { createExtensionElement, umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; +import type { ManifestPropertyEditorUI, ManifestTypes } from '@umbraco-cms/models'; import '../../property-actions/shared/property-action-menu/property-action-menu.element'; import '../../../../backoffice/shared/components/workspace/workspace-property-layout/workspace-property-layout.element'; import { UmbObserverController } from '@umbraco-cms/observable-api'; import { UmbLitElement } from '@umbraco-cms/element'; +import { DataTypePropertyModel } from '@umbraco-cms/backend-api'; /** * @element umb-workspace-property @@ -94,11 +95,11 @@ export class UmbWorkspacePropertyElement extends UmbLitElement { * @attr * @default '' */ - private _propertyEditorUIAlias = ''; + private _propertyEditorUiAlias = ''; @property({ type: String, attribute: 'property-editor-ui-alias' }) - public set propertyEditorUIAlias(value: string) { - if (this._propertyEditorUIAlias === value) return; - this._propertyEditorUIAlias = value; + public set propertyEditorUiAlias(value: string) { + if (this._propertyEditorUiAlias === value) return; + this._propertyEditorUiAlias = value; this._observePropertyEditorUI(); } @@ -122,7 +123,7 @@ export class UmbWorkspacePropertyElement extends UmbLitElement { * @default '' */ @property({ type: Object, attribute: false }) - public set config(value: DataTypePropertyData[]) { + public set config(value: DataTypePropertyModel[]) { this._propertyContext.setConfig(value); } @@ -158,7 +159,7 @@ export class UmbWorkspacePropertyElement extends UmbLitElement { private _observePropertyEditorUI() { this.propertyEditorUIObserver?.destroy(); this.propertyEditorUIObserver = this.observe( - umbExtensionsRegistry.getByTypeAndAlias('propertyEditorUI', this._propertyEditorUIAlias), + umbExtensionsRegistry.getByTypeAndAlias('propertyEditorUI', this._propertyEditorUiAlias), (manifest) => { this._gotEditorUI(manifest); } @@ -167,7 +168,7 @@ export class UmbWorkspacePropertyElement extends UmbLitElement { private _gotEditorUI(manifest?: ManifestPropertyEditorUI | null) { if (!manifest) { - // TODO: if propertyEditorUIAlias didn't exist in store, we should do some nice fail UI. + // TODO: if propertyEditorUiAlias didn't exist in store, we should do some nice fail UI. return; } @@ -217,11 +218,11 @@ export class UmbWorkspacePropertyElement extends UmbLitElement { } private _renderPropertyActionMenu() { - return html`${this._propertyEditorUIAlias + return html`${this._propertyEditorUiAlias ? html`` : ''}`; } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/collection/workspace-view-collection.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/collection/workspace-view-collection.element.ts index 12def9037f..c171c79007 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/collection/workspace-view-collection.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/collection/workspace-view-collection.element.ts @@ -7,7 +7,6 @@ import { UMB_COLLECTION_CONTEXT_TOKEN, } from '../../../../../../shared/collection/collection.context'; -import '../../../../../../shared/components/content-property/content-property.element'; import '../../../../../../shared/collection/dashboards/dashboard-collection.element'; import type { UmbWorkspaceEntityContextInterface } from '../../../workspace-context/workspace-entity-context.interface'; import { UmbLitElement } from '@umbraco-cms/element'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/edit/workspace-view-content-edit.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/edit/workspace-view-content-edit.element.ts index b915b9fd97..b974eda86c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/edit/workspace-view-content-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/edit/workspace-view-content-edit.element.ts @@ -3,9 +3,8 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, state } from 'lit/decorators.js'; import { repeat } from 'lit/directives/repeat.js'; import type { UmbWorkspaceEntityContextInterface } from '../../../workspace-context/workspace-entity-context.interface'; -import type { ContentProperty, ContentPropertyData, DocumentDetails, MediaDetails } from '@umbraco-cms/models'; +import type { ContentProperty, ContentPropertyData, MediaTypeDetails } from '@umbraco-cms/models'; -import '../../../../content-property/content-property.element'; import { UmbLitElement } from '@umbraco-cms/element'; @customElement('umb-workspace-view-content-edit') @@ -26,17 +25,16 @@ export class UmbWorkspaceViewContentEditElement extends UmbLitElement { @state() _data: ContentPropertyData[] = []; - private _workspaceContext?: UmbWorkspaceEntityContextInterface; + private _workspaceContext?: UmbWorkspaceEntityContextInterface; constructor() { super(); // TODO: Figure out how to get the magic string for the workspace context. - this.consumeContext>( + this.consumeContext>( 'umbWorkspaceContext', (workspaceContext) => { this._workspaceContext = workspaceContext; - console.log("workspaceContext", workspaceContext) this._observeContent(); } ); @@ -52,17 +50,22 @@ export class UmbWorkspaceViewContentEditElement extends UmbLitElement { Should use a Observable for example: this._workspaceContext.properties */ - this.observe(this._workspaceContext.data, (content) => { - this._properties = content?.properties || []; - this._data = content?.data || []; - console.log("content", content) + /* + // TODO: broken for now, as we need to transfer into the repository way: + this.observe( + this._workspaceContext.data, + (content) => { + // TODO: Should be adapted to new models, maybe a shared 'Content' solution is not the right thing here. + this._properties = content?.properties || []; + console.log('content', content); - /* - 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'); + //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' + ); + */ } render() { @@ -72,9 +75,7 @@ export class UmbWorkspaceViewContentEditElement extends UmbLitElement { this._properties, (property) => property.alias, (property) => - html` data.alias === property.alias)?.value}> ` + html` ` )} `; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/info/workspace-view-content-info.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/info/workspace-view-content-info.element.ts index 8db59d9664..3299a3d858 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/info/workspace-view-content-info.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/info/workspace-view-content-info.element.ts @@ -2,7 +2,7 @@ import { css, html } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, state } from 'lit/decorators.js'; import { UmbWorkspaceEntityContextInterface } from '../../../workspace-context/workspace-entity-context.interface'; -import type { DocumentDetails, MediaDetails } from '@umbraco-cms/models'; +import type { DocumentModel } from '@umbraco-cms/backend-api'; import { UmbLitElement } from '@umbraco-cms/element'; @customElement('umb-workspace-view-content-info') @@ -20,27 +20,27 @@ export class UmbWorkspaceViewContentInfoElement extends UmbLitElement { @state() private _nodeName = ''; - private _workspaceContext?: UmbWorkspaceEntityContextInterface; + private _workspaceContext?: UmbWorkspaceEntityContextInterface; constructor() { super(); // TODO: Figure out how to get the magic string for the workspace context. - this.consumeContext>( - 'umbWorkspaceContext', - (nodeContext) => { - this._workspaceContext = nodeContext; - this._observeContent(); - } - ); + this.consumeContext>('umbWorkspaceContext', (nodeContext) => { + this._workspaceContext = nodeContext; + this._observeContent(); + }); } private _observeContent() { if (!this._workspaceContext) return; + this._nodeName = 'TBD, with variants this is not as simple.'; + /* this.observe(this._workspaceContext.name, (name) => { this._nodeName = name || ''; }); + */ } render() { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-context.interface.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-context.interface.ts index af0d80e3cd..65dfa62416 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-context.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-context.interface.ts @@ -1,10 +1,5 @@ -import type { Observable } from "rxjs"; - export interface UmbWorkspaceContextInterface { - - readonly data: Observable; - - + //readonly data: Observable; //getUnique(): string | undefined; getEntityType(): string; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-entity-context.interface.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-entity-context.interface.ts index 5d74974e45..87fd3e526e 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-entity-context.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-entity-context.interface.ts @@ -1,8 +1,7 @@ -import type { Observable } from 'rxjs'; import { UmbWorkspaceContextInterface } from './workspace-context.interface'; export interface UmbWorkspaceEntityContextInterface extends UmbWorkspaceContextInterface { - readonly name: Observable; + //readonly name: Observable; getEntityKey(): string | undefined; // COnsider if this should go away now that we have getUnique() getEntityType(): string; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.element.ts index 585629cb9f..1320ed1f17 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.element.ts @@ -46,7 +46,7 @@ export class UmbPropertyActionMenuElement extends UmbLitElement { public value?: string; @property() - set propertyEditorUIAlias(alias: string) { + set propertyEditorUiAlias(alias: string) { this._observeActions(alias); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/shared/property-editor-config/property-editor-config.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/shared/property-editor-config/property-editor-config.element.ts index 90894acd9d..ad3c265dbb 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/shared/property-editor-config/property-editor-config.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/shared/property-editor-config/property-editor-config.element.ts @@ -22,15 +22,15 @@ export class UmbPropertyEditorConfigElement extends UmbLitElement { * @attr * @default '' */ - private _propertyEditorUIAlias = ''; + private _propertyEditorUiAlias = ''; @property({ type: String, attribute: 'property-editor-ui-alias' }) - public get propertyEditorUIAlias(): string { - return this._propertyEditorUIAlias; + public get propertyEditorUiAlias(): string { + return this._propertyEditorUiAlias; } - public set propertyEditorUIAlias(value: string) { - const oldVal = this._propertyEditorUIAlias; - this._propertyEditorUIAlias = value; - this.requestUpdate('propertyEditorUIAlias', oldVal); + public set propertyEditorUiAlias(value: string) { + const oldVal = this._propertyEditorUiAlias; + this._propertyEditorUiAlias = value; + this.requestUpdate('propertyEditorUiAlias', oldVal); this._observePropertyEditorUIConfig(); } @@ -56,10 +56,10 @@ export class UmbPropertyEditorConfigElement extends UmbLitElement { private _propertyEditorUIConfigProperties: Array = []; private _observePropertyEditorUIConfig() { - if (!this._propertyEditorUIAlias) return; + if (!this._propertyEditorUiAlias) return; this.observe( - umbExtensionsRegistry.getByTypeAndAlias('propertyEditorUI', this.propertyEditorUIAlias), + umbExtensionsRegistry.getByTypeAndAlias('propertyEditorUI', this.propertyEditorUiAlias), (manifest) => { this._observePropertyEditorModelConfig(manifest?.meta.propertyEditorModel); this._propertyEditorUIConfigProperties = manifest?.meta.config?.properties || []; @@ -70,18 +70,15 @@ export class UmbPropertyEditorConfigElement extends UmbLitElement { ); } - private _observePropertyEditorModelConfig(propertyEditorModelAlias?: string) { - if (!propertyEditorModelAlias) return; + private _observePropertyEditorModelConfig(propertyEditorAlias?: string) { + if (!propertyEditorAlias) return; - this.observe( - umbExtensionsRegistry.getByTypeAndAlias('propertyEditorModel', propertyEditorModelAlias), - (manifest) => { - this._propertyEditorModelConfigProperties = manifest?.meta.config?.properties || []; - this._propertyEditorModelConfigDefaultData = manifest?.meta.config?.defaultData || []; - this._mergeConfigProperties(); - this._mergeConfigDefaultData(); - } - ); + this.observe(umbExtensionsRegistry.getByTypeAndAlias('propertyEditorModel', propertyEditorAlias), (manifest) => { + this._propertyEditorModelConfigProperties = manifest?.meta.config?.properties || []; + this._propertyEditorModelConfigDefaultData = manifest?.meta.config?.defaultData || []; + this._mergeConfigProperties(); + this._mergeConfigDefaultData(); + }); } private _mergeConfigProperties() { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/checkbox-list/property-editor-ui-checkbox-list.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/checkbox-list/property-editor-ui-checkbox-list.element.ts index c990aeb92b..375dde3ff2 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/checkbox-list/property-editor-ui-checkbox-list.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/checkbox-list/property-editor-ui-checkbox-list.element.ts @@ -2,9 +2,9 @@ import { html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import '../../../components/input-checkbox-list/input-checkbox-list.element'; -import { UmbInputCheckboxListElement } from '../../../components/input-checkbox-list/input-checkbox-list.element'; +import type { UmbInputCheckboxListElement } from '../../../components/input-checkbox-list/input-checkbox-list.element'; import { UmbLitElement } from '@umbraco-cms/element'; -import type { DataTypePropertyData } from '@umbraco-cms/models'; +import type { DataTypePropertyModel } from '@umbraco-cms/backend-api'; /** * @element umb-property-editor-ui-checkbox-list @@ -23,7 +23,7 @@ export class UmbPropertyEditorUICheckboxListElement extends UmbLitElement { } @property({ type: Array, attribute: false }) - public set config(config: Array) { + public set config(config: Array) { const listData = config.find((x) => x.alias === 'itemList'); if (!listData) return; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/color-picker/property-editor-ui-color-picker.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/color-picker/property-editor-ui-color-picker.element.ts index f73d8a6016..8d843e896f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/color-picker/property-editor-ui-color-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/color-picker/property-editor-ui-color-picker.element.ts @@ -1,11 +1,10 @@ import { html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; -import { ifDefined } from 'lit/directives/if-defined'; import { UUIColorSwatchesEvent } from '@umbraco-ui/uui'; +import '../../../../shared/components/color-picker/color-picker.element'; import { UmbLitElement } from '@umbraco-cms/element'; -import type { DataTypePropertyData } from '@umbraco-cms/models'; -import type { UmbColorPickerElement } from 'src/backoffice/shared/components/color-picker/color-picker.element'; +import type { DataTypePropertyModel } from '@umbraco-cms/backend-api'; /** * @element umb-property-editor-ui-color-picker @@ -24,7 +23,7 @@ export class UmbPropertyEditorUIColorPickerElement extends UmbLitElement { private _colorSwatches: string[] = []; @property({ type: Array, attribute: false }) - public set config(config: Array) { + public set config(config: Array) { const includeLabels = config.find((x) => x.alias === 'includeLabels'); if (includeLabels) this._includeLabels = includeLabels.value; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/document-picker/property-editor-ui-document-picker.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/document-picker/property-editor-ui-document-picker.element.ts index 202b47cbef..f6edebbb17 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/document-picker/property-editor-ui-document-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/document-picker/property-editor-ui-document-picker.element.ts @@ -1,8 +1,9 @@ import { html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; -import { UmbInputDocumentPickerElement } from '../../../components/input-document-picker/input-document-picker.element'; +import type { UmbInputDocumentPickerElement } from '../../../components/input-document-picker/input-document-picker.element'; import { UmbLitElement } from '@umbraco-cms/element'; -import type { DataTypePropertyData } from '@umbraco-cms/models'; +import '../../../components/input-document-picker/input-document-picker.element'; +import type { DataTypePropertyModel } from '@umbraco-cms/backend-api'; @customElement('umb-property-editor-ui-document-picker') export class UmbPropertyEditorUIContentPickerElement extends UmbLitElement { @@ -17,7 +18,7 @@ export class UmbPropertyEditorUIContentPickerElement extends UmbLitElement { } @property({ type: Array, attribute: false }) - public set config(config: Array) { + public set config(config: Array) { const validationLimit = config.find((x) => x.alias === 'validationLimit'); this._limitMin = (validationLimit?.value as any).min; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/eye-dropper/property-editor-ui-eye-dropper.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/eye-dropper/property-editor-ui-eye-dropper.element.ts index 8ad1e00d87..d69e7f471a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/eye-dropper/property-editor-ui-eye-dropper.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/eye-dropper/property-editor-ui-eye-dropper.element.ts @@ -4,7 +4,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { UUIColorPickerChangeEvent } from '@umbraco-ui/uui'; import { UmbLitElement } from '@umbraco-cms/element'; import '../../../components/eye-dropper/eye-dropper.element'; -import type { DataTypePropertyData } from '@umbraco-cms/models'; +import type { DataTypePropertyModel } from '@umbraco-cms/backend-api'; /** * @element umb-property-editor-ui-eye-dropper @@ -23,7 +23,7 @@ export class UmbPropertyEditorUIEyeDropperElement extends UmbLitElement { private _swatches: string[] = []; @property({ type: Array, attribute: false }) - public set config(config: Array) { + public set config(config: Array) { const showAlpha = config.find((x) => x.alias === 'showAlpha'); if (showAlpha) this._opacity = showAlpha.value; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/media-picker/property-editor-ui-media-picker.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/media-picker/property-editor-ui-media-picker.element.ts index e96df3ce5f..ee2fcef3ba 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/media-picker/property-editor-ui-media-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/media-picker/property-editor-ui-media-picker.element.ts @@ -2,7 +2,7 @@ import { html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { UmbInputMediaPickerElement } from '../../../../../backoffice/shared/components/input-media-picker/input-media-picker.element'; import { UmbLitElement } from '@umbraco-cms/element'; -import type { DataTypePropertyData } from '@umbraco-cms/models'; +import type { DataTypePropertyModel } from '@umbraco-cms/backend-api'; /** * @element umb-property-editor-ui-media-picker @@ -20,7 +20,7 @@ export class UmbPropertyEditorUIMediaPickerElement extends UmbLitElement { } @property({ type: Array, attribute: false }) - public set config(config: Array) { + public set config(config: Array) { const validationLimit = config.find((x) => x.alias === 'validationLimit'); if (!validationLimit) return; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multiple-text-string/input-multiple-text-string/input-multiple-text-string.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multiple-text-string/input-multiple-text-string/input-multiple-text-string.element.ts index b3643dbe58..ff51c6f394 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multiple-text-string/input-multiple-text-string/input-multiple-text-string.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multiple-text-string/input-multiple-text-string/input-multiple-text-string.element.ts @@ -7,7 +7,6 @@ import UmbInputMultipleTextStringItemElement from '../input-multiple-text-string import { UmbInputEvent, UmbChangeEvent, UmbDeleteEvent } from '../../../../../../core/events'; import { UmbLitElement } from '@umbraco-cms/element'; -import '../input-multiple-text-string-item/input-multiple-text-string-item.element'; export type MultipleTextStringValue = Array; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multiple-text-string/property-editor-ui-multiple-text-string.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multiple-text-string/property-editor-ui-multiple-text-string.element.ts index f74398bdcf..2b9cf0e509 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multiple-text-string/property-editor-ui-multiple-text-string.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/multiple-text-string/property-editor-ui-multiple-text-string.element.ts @@ -8,7 +8,6 @@ import UmbInputMultipleTextStringElement, { import { UmbChangeEvent } from 'src/core/events/change.event'; import { UmbLitElement } from '@umbraco-cms/element'; -import './input-multiple-text-string/input-multiple-text-string.element'; export type MultipleTextStringConfigData = Array<{ alias: 'minNumber' | 'maxNumber'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/template.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/template.repository.ts index d3a06e755b..6b31e2b6bd 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/template.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/template.repository.ts @@ -122,7 +122,7 @@ export class UmbTemplateRepository implements UmbTreeRepository, UmbDetailReposi return this.#detailDataSource.createScaffold(); } - 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/templating/templates/workspace/data/sources/template.detail.server.data.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/workspace/data/sources/template.detail.server.data.ts index 54b250c4fa..acbc907fe3 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/workspace/data/sources/template.detail.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/workspace/data/sources/template.detail.server.data.ts @@ -39,7 +39,7 @@ export class UmbTemplateDetailServerDataSource implements TemplateDetailDataSour * @memberof UmbTemplateDetailServerDataSource */ async createScaffold() { - let error = undefined; + const error = undefined; const data: TemplateModel = { key: uuid(), name: '', @@ -47,13 +47,17 @@ export class UmbTemplateDetailServerDataSource implements TemplateDetailDataSour content: '', }; + // TODO: update when backend is updated so we don't have to do two calls + /* + // TODO: Revisit template models, masterTemplateAlias is not here anymore? const { data: scaffoldData, error: scaffoldError } = await tryExecuteAndNotify( this.#host, TemplateResource.getTemplateScaffold() ); + */ - error = scaffoldError; - data.content = scaffoldData?.content || ''; + //error = scaffoldError; + //data.content = scaffoldData?.content || ''; return { data, error }; } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.element.ts index 3a04cbc4e5..3d8e75c009 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.element.ts @@ -204,7 +204,7 @@ export class UmbUserGroupWorkspaceElement extends UmbLitElement implements UmbWo this._observeUsers(); }); - this.observe(this._workspaceContext.data.pipe(distinctUntilChanged()), (userGroup) => { + this.observe(this._workspaceContext.data, (userGroup) => { this._userGroup = userGroup; }); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/user.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/user.store.ts index 2a74eafda8..2978ebf035 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/user.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/user.store.ts @@ -15,22 +15,18 @@ export const UMB_USER_STORE_CONTEXT_TOKEN = new UmbContextToken('U * @description - Data Store for Users */ export class UmbUserStore extends UmbStoreBase implements UmbEntityDetailStore { - - - #users = new ArrayState([], x => x.key); + #users = new ArrayState([], (x) => x.key); public users = this.#users.asObservable(); #totalUsers = new NumberState(0); public readonly totalUsers = this.#totalUsers.asObservable(); - constructor(host: UmbControllerHostInterface) { super(host, UMB_USER_STORE_CONTEXT_TOKEN.toString()); } - getScaffold(entityType: string, parentKey: string | null) { - return { + return { key: '', name: '', icon: '', @@ -49,7 +45,6 @@ export class UmbUserStore extends UmbStoreBase implements UmbEntityDetailStore)} + * @return {*} {(Observable)} * @memberof UmbDataTypeStore */ getByKey(key: string) { @@ -78,10 +73,11 @@ export class UmbUserStore extends UmbStoreBase implements UmbEntityDetailStore) => users.find((user: UmbUserStoreItemType) => user.key === key)); + return this.#users.getObservablePart((users: Array) => + users.find((user: UmbUserStoreItemType) => user.key === key) + ); } - /** * @description - Request Users by keys. * @param {string} key @@ -96,7 +92,9 @@ export class UmbUserStore extends UmbStoreBase implements UmbEntityDetailStore) => users.filter((user: UmbUserStoreItemType) => keys.includes(user.key))); + return this.#users.getObservablePart((users: Array) => + users.filter((user: UmbUserStoreItemType) => keys.includes(user.key)) + ); } getByName(name: string) { @@ -110,7 +108,9 @@ export class UmbUserStore extends UmbStoreBase implements UmbEntityDetailStore) => users.filter((user: UmbUserStoreItemType) => user.name.toLocaleLowerCase().includes(name))); + return this.#users.getObservablePart((users: Array) => + users.filter((user: UmbUserStoreItemType) => user.name.toLocaleLowerCase().includes(name)) + ); } async enableUsers(userKeys: Array) { @@ -243,12 +243,7 @@ export class UmbUserStore extends UmbStoreBase implements UmbEntityDetailStore - ) { + async invite(name: string, email: string, message: string, userGroups: Array) { // TODO: use Fetcher API. try { const res = await fetch('/umbraco/backoffice/users/invite', { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.element.ts index b53c8bf89a..50b0da7e3f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.element.ts @@ -88,7 +88,6 @@ export class UmbUserWorkspaceElement extends UmbLitElement implements UmbWorkspa private _languages = []; //TODO Add languages - private _workspaceContext: UmbWorkspaceUserContext = new UmbWorkspaceUserContext(this); @state() @@ -105,7 +104,7 @@ export class UmbUserWorkspaceElement extends UmbLitElement implements UmbWorkspa this._observeCurrentUser(); }); - this.observe(this._workspaceContext.data.pipe(distinctUntilChanged()), (user) => { + this.observe(this._workspaceContext.data, (user) => { this._user = user; if (user && user.name !== this._userName) { this._userName = user.name; diff --git a/src/Umbraco.Web.UI.Client/src/core/mocks/data/data-type.data.ts b/src/Umbraco.Web.UI.Client/src/core/mocks/data/data-type.data.ts index 18620770db..7d439cde58 100644 --- a/src/Umbraco.Web.UI.Client/src/core/mocks/data/data-type.data.ts +++ b/src/Umbraco.Web.UI.Client/src/core/mocks/data/data-type.data.ts @@ -1,20 +1,22 @@ import { UmbEntityData } from './entity.data'; import { createFolderTreeItem } from './utils'; -import type { FolderTreeItemModel } from '@umbraco-cms/backend-api'; -import type { DataTypeDetails } from '@umbraco-cms/models'; +import type { FolderTreeItemModel, DataTypeModel } from '@umbraco-cms/backend-api'; -export const data: Array = [ +export const data: Array = [ + { + key: '0cc0eba1-9960-42c9-bf9b-60e150b429ae', + parentKey: null, + name: 'Textstring', + propertyEditorAlias: 'Umbraco.TextBox', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.TextBox', + data: [], + }, { name: 'Text', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-textBox', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.TextBox', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.TextBox', + propertyEditorAlias: 'Umbraco.TextBox', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.TextBox', data: [ { alias: 'maxChars', @@ -24,41 +26,26 @@ export const data: Array = [ }, { name: 'Text Area', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-textArea', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.TextArea', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.TextArea', + propertyEditorAlias: 'Umbraco.TextArea', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.TextArea', data: [], }, { name: 'My JS Property Editor', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-custom', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.JSON', - propertyEditorUIAlias: 'My.PropertyEditorUI.Custom', + propertyEditorAlias: 'Umbraco.JSON', + propertyEditorUiAlias: 'My.PropertyEditorUI.Custom', data: [], }, { name: 'Color Picker', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-colorPicker', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.ColorPicker', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.ColorPicker', + propertyEditorAlias: 'Umbraco.ColorPicker', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.ColorPicker', data: [ { alias: 'includeLabels', @@ -72,15 +59,10 @@ export const data: Array = [ }, { name: 'Content Picker', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-contentPicker', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.ContentPicker', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.DocumentPicker', + propertyEditorAlias: 'Umbraco.ContentPicker', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.DocumentPicker', data: [ { alias: 'validationLimit', @@ -90,15 +72,10 @@ export const data: Array = [ }, { name: 'Eye Dropper', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-eyeDropper', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.ColorPicker.EyeDropper', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.EyeDropper', + propertyEditorAlias: 'Umbraco.ColorPicker.EyeDropper', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.EyeDropper', data: [ { alias: 'palette', @@ -129,67 +106,42 @@ export const data: Array = [ }, { name: 'Multi URL Picker', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-multiUrlPicker', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.MultiUrlPicker', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.MultiUrlPicker', + propertyEditorAlias: 'Umbraco.MultiUrlPicker', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.MultiUrlPicker', data: [], }, { name: 'Multi Node Tree Picker', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-multiNodeTreePicker', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.MultiNodeTreePicker', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.TreePicker', + propertyEditorAlias: 'Umbraco.MultiNodeTreePicker', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.TreePicker', data: [], }, { name: 'Date Picker', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-datePicker', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.DateTime', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.DatePicker', + propertyEditorAlias: 'Umbraco.DateTime', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.DatePicker', data: [], }, { name: 'Email', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-email', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.EmailAddress', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.Email', + propertyEditorAlias: 'Umbraco.EmailAddress', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.Email', data: [], }, { name: 'Multiple Text String', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-multipleTextString', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.MultipleTextString', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.MultipleTextString', + propertyEditorAlias: 'Umbraco.MultipleTextString', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.MultipleTextString', data: [ { alias: 'minNumber', @@ -203,93 +155,58 @@ export const data: Array = [ }, { name: 'Dropdown', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-dropdown', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.DropDown.Flexible', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.Dropdown', + propertyEditorAlias: 'Umbraco.DropDown.Flexible', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.Dropdown', data: [], }, { name: 'Slider', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-slider', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.Slider', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.Slider', + propertyEditorAlias: 'Umbraco.Slider', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.Slider', data: [], }, { name: 'Toggle', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-toggle', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.TrueFalse', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.Toggle', + propertyEditorAlias: 'Umbraco.TrueFalse', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.Toggle', data: [], }, { name: 'Tags', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-tags', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.Tags', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.Tags', + propertyEditorAlias: 'Umbraco.Tags', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.Tags', data: [], }, { name: 'Markdown Editor', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-markdownEditor', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.MarkdownEditor', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.MarkdownEditor', + propertyEditorAlias: 'Umbraco.MarkdownEditor', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.MarkdownEditor', data: [], }, { name: 'Radio Button List', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-radioButtonList', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.RadioButtonList', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.RadioButtonList', + propertyEditorAlias: 'Umbraco.RadioButtonList', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.RadioButtonList', data: [], }, { name: 'Checkbox List', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-checkboxList', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.CheckboxList', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.CheckboxList', + propertyEditorAlias: 'Umbraco.CheckboxList', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.CheckboxList', data: [ { alias: 'itemList', @@ -302,223 +219,138 @@ export const data: Array = [ }, { name: 'Block List', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-blockList', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.BlockList', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.BlockList', + propertyEditorAlias: 'Umbraco.BlockList', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.BlockList', data: [], }, { name: 'Media Picker', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-mediaPicker', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.MediaPicker3', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.MediaPicker', + propertyEditorAlias: 'Umbraco.MediaPicker3', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.MediaPicker', data: [], }, { name: 'Image Cropper', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-imageCropper', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.ImageCropper', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.ImageCropper', + propertyEditorAlias: 'Umbraco.ImageCropper', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.ImageCropper', data: [], }, { name: 'Upload Field', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-uploadField', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.UploadField', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.UploadField', + propertyEditorAlias: 'Umbraco.UploadField', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.UploadField', data: [], }, { name: 'Block Grid', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-blockGrid', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.BlockGrid', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.BlockGrid', + propertyEditorAlias: 'Umbraco.BlockGrid', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.BlockGrid', data: [], }, { name: 'Collection View', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-collectionView', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.ListView', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.CollectionView', + propertyEditorAlias: 'Umbraco.ListView', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.CollectionView', data: [], }, { name: 'Icon Picker', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-iconPicker', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.IconPicker', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.IconPicker', + propertyEditorAlias: 'Umbraco.IconPicker', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.IconPicker', data: [], }, { name: 'Number Range', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-numberRange', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.JSON', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.NumberRange', + propertyEditorAlias: 'Umbraco.JSON', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.NumberRange', data: [], }, { name: 'Order Direction', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-orderDirection', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.JSON', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.OrderDirection', + propertyEditorAlias: 'Umbraco.JSON', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.OrderDirection', data: [], }, { name: 'Overlay Size', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-overlaySize', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.JSON', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.OverlaySize', + propertyEditorAlias: 'Umbraco.JSON', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.OverlaySize', data: [], }, { name: 'Rich Text Editor', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-richTextEditor', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.TinyMCE', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.TinyMCE', + propertyEditorAlias: 'Umbraco.TinyMCE', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.TinyMCE', data: [], }, { name: 'Label', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-label', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.Label', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.Label', + propertyEditorAlias: 'Umbraco.Label', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.Label', data: [], }, { name: 'Integer', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-integer', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.Integer', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.Integer', + propertyEditorAlias: 'Umbraco.Integer', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.Integer', data: [], }, { name: 'Decimal', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-decimal', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.Decimal', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.Decimal', + propertyEditorAlias: 'Umbraco.Decimal', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.Decimal', data: [], }, { name: 'User Picker', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-userPicker', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.UserPicker', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.UserPicker', + propertyEditorAlias: 'Umbraco.UserPicker', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.UserPicker', data: [], }, { name: 'Member Picker', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-memberPicker', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.MemberPicker', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.MemberPicker', + propertyEditorAlias: 'Umbraco.MemberPicker', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.MemberPicker', data: [], }, { name: 'Member Group Picker', - type: 'data-type', - icon: 'umb:autofill', - hasChildren: false, key: 'dt-memberGroupPicker', - isContainer: false, parentKey: null, - isFolder: false, - propertyEditorModelAlias: 'Umbraco.MemberGroupPicker', - propertyEditorUIAlias: 'Umb.PropertyEditorUI.MemberGroupPicker', + propertyEditorAlias: 'Umbraco.MemberGroupPicker', + propertyEditorUiAlias: 'Umb.PropertyEditorUI.MemberGroupPicker', data: [], }, ]; @@ -527,7 +359,7 @@ export const data: Array = [ // TODO: all properties are optional in the server schema. I don't think this is correct. // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -class UmbDataTypeData extends UmbEntityData { +class UmbDataTypeData extends UmbEntityData { constructor() { super(data); } diff --git a/src/Umbraco.Web.UI.Client/src/core/mocks/data/document-type.data.ts b/src/Umbraco.Web.UI.Client/src/core/mocks/data/document-type.data.ts index da3c6a6daf..ba55f2165e 100644 --- a/src/Umbraco.Web.UI.Client/src/core/mocks/data/document-type.data.ts +++ b/src/Umbraco.Web.UI.Client/src/core/mocks/data/document-type.data.ts @@ -1,9 +1,708 @@ import { UmbEntityData } from './entity.data'; import { createDocumentTypeTreeItem } from './utils'; -import type { DocumentTypeTreeItemModel } from '@umbraco-cms/backend-api'; -import type { DocumentTypeDetails } from '@umbraco-cms/models'; +import { + DocumentTypeTreeItemModel, + DocumentTypeModel, + ContentTypeCompositionTypeModel, +} from '@umbraco-cms/backend-api'; -export const data: Array = [ +export const data: Array = [ + { + allowedTemplateKeys: [], + defaultTemplateKey: null, + key: 'all-property-editors-document-type-key', + alias: 'blogPost', + name: 'Blog Post', + description: null, + icon: 'icon-item-arrangement', + allowedAsRoot: true, + variesByCulture: true, + variesBySegment: false, + isElement: false, + properties: [ + { + key: '2', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'colorPicker', + name: 'Color Picker', + description: '', + dataTypeKey: 'dt-colorPicker', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '3', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'contentPicker', + name: 'Content Picker', + description: '', + dataTypeKey: 'dt-contentPicker', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '4', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'eyeDropper', + name: 'Eye Dropper', + description: '', + dataTypeKey: 'dt-eyeDropper', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '5', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'multiUrlPicker', + name: 'Multi URL Picker', + description: '', + dataTypeKey: 'dt-multiUrlPicker', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '6', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'multiNodeTreePicker', + name: 'Multi Node Tree Picker', + description: '', + dataTypeKey: 'dt-multiNodeTreePicker', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '7', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'datePicker', + name: 'Date Picker', + description: '', + dataTypeKey: 'dt-datePicker', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '8', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'email', + name: 'Email', + description: '', + dataTypeKey: 'dt-email', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '9', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'textBox', + name: 'Text Box', + description: '', + dataTypeKey: 'dt-textBox', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '19', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'dropdown', + name: 'Dropdown', + description: '', + dataTypeKey: 'dt-dropdown', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '11', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'textArea', + name: 'Text Area', + description: '', + dataTypeKey: 'dt-textArea', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '12', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'slider', + name: 'Slider', + description: '', + dataTypeKey: 'dt-slider', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '13', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'toggle', + name: 'Toggle', + description: '', + dataTypeKey: 'dt-toggle', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '14', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'tags', + name: 'Tags', + description: '', + dataTypeKey: 'dt-tags', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '15', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'markdownEditor', + name: 'MarkdownEditor', + description: '', + dataTypeKey: 'dt-markdownEditor', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '16', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'radioButtonList', + name: 'Radio Button List', + description: '', + dataTypeKey: 'dt-radioButtonList', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '17', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'checkboxList', + name: 'Checkbox List', + description: '', + dataTypeKey: 'dt-checkboxList', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '18', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'blockList', + name: 'Block List', + description: '', + dataTypeKey: 'dt-blockList', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '19', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'mediaPicker', + name: 'Media Picker', + description: '', + dataTypeKey: 'dt-mediaPicker', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '20', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'imageCropper', + name: 'Image Cropper', + description: '', + dataTypeKey: 'dt-imageCropper', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '21', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'uploadField', + name: 'Upload Field', + description: '', + dataTypeKey: 'dt-uploadField', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '22', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'blockGrid', + name: 'Block Grid', + description: '', + dataTypeKey: 'dt-blockGrid', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '23', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'blockGrid', + name: 'Icon Picker', + description: '', + dataTypeKey: 'dt-iconPicker', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '24', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'numberRange', + name: 'Number Range', + description: '', + dataTypeKey: 'dt-numberRange', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '25', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'orderDirection', + name: 'Order Direction', + description: '', + dataTypeKey: 'dt-orderDirection', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '26', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'overlaySize', + name: 'Overlay Size', + description: '', + dataTypeKey: 'dt-overlaySize', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '27', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'label', + name: 'Label', + description: '', + dataTypeKey: 'dt-label', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '28', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'integer', + name: 'Integer', + description: '', + dataTypeKey: 'dt-integer', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '29', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'decimal', + name: 'Decimal', + description: '', + dataTypeKey: 'dt-decimal', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '30', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'memberPicker', + name: 'Member Picker', + description: '', + dataTypeKey: 'dt-memberPicker', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '31', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'memberGroupPicker', + name: 'Member Group Picker', + description: '', + dataTypeKey: 'dt-memberGroupPicker', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '32', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'userPicker', + name: 'User Picker', + description: '', + dataTypeKey: 'dt-userPicker', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + ], + containers: [ + { + key: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + parentKey: null, + name: 'Content', + type: 'Group', + sortOrder: 0, + }, + ], + allowedContentTypes: [], + compositions: [], + cleanup: { + preventCleanup: false, + keepAllVersionsNewerThanDays: null, + keepLatestVersionPerDayForDays: null, + }, + }, + + { + allowedTemplateKeys: [], + defaultTemplateKey: null, + key: '29643452-cff9-47f2-98cd-7de4b6807681', + alias: 'blogPost', + name: 'Blog Post', + description: null, + icon: 'icon-item-arrangement', + allowedAsRoot: true, + variesByCulture: true, + variesBySegment: false, + isElement: false, + properties: [ + { + key: '5b4ca208-134e-4865-b423-06e5e97adf3c', + containerKey: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + alias: 'blogPostText', + name: 'Blog Post Text', + description: null, + dataTypeKey: '0cc0eba1-9960-42c9-bf9b-60e150b429ae', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: 'ef7096b6-7c9e-49ba-8d49-395111e65ea2', + containerKey: '227d6ed2-e118-4494-b8f2-deb69854a56a', + alias: 'blogTextStringUnderMasterTab', + name: 'Blog text string under master tab', + description: null, + dataTypeKey: '0cc0eba1-9960-42c9-bf9b-60e150b429ae', + validation: { + mandatory: false, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: 'e010c429-b298-499a-9bfe-79687af8972a', + containerKey: '22177c49-ecba-4f2e-b7fa-3f2c04d02cfb', + alias: 'blogTextStringUnderGroupUnderMasterTab', + name: 'Blog text string under group under master tab', + description: null, + dataTypeKey: '0cc0eba1-9960-42c9-bf9b-60e150b429ae', + validation: { + mandatory: false, + mandatoryMessage: null, + regEx: null, + regExMessage: null, + }, + appearance: { + labelOnTop: false, + }, + }, + { + key: '1a22749a-c7d2-44bb-b36b-c977c2ced6ef', + containerKey: '2c943997-b685-432d-a6c5-601d8e7a298a', + alias: 'localBlogTabString', + name: 'Local Blog Tab String', + description: null, + dataTypeKey: '0cc0eba1-9960-42c9-bf9b-60e150b429ae', + validation: { + mandatory: true, + mandatoryMessage: null, + regEx: '^[0-9]*$', + regExMessage: null, + }, + appearance: { + labelOnTop: true, + }, + }, + ], + containers: [ + { + key: 'c3cd2f12-b7c4-4206-8d8b-27c061589f75', + parentKey: null, + name: 'Content', + type: 'Group', + sortOrder: 0, + }, + { + key: '227d6ed2-e118-4494-b8f2-deb69854a56a', + parentKey: null, + name: 'Master Tab', + type: 'Tab', + sortOrder: 0, + }, + { + key: '22177c49-ecba-4f2e-b7fa-3f2c04d02cfb', + parentKey: '227d6ed2-e118-4494-b8f2-deb69854a56a', + name: 'Blog Group under master tab', + type: 'Group', + sortOrder: 0, + }, + { + key: '2c943997-b685-432d-a6c5-601d8e7a298a', + parentKey: null, + name: 'Local blog tab', + type: 'Tab', + sortOrder: 1, + }, + ], + allowedContentTypes: [ + { + key: '29643452-cff9-47f2-98cd-7de4b6807681', + sortOrder: 0, + }, + ], + compositions: [ + { + key: '5035d7d9-0a63-415c-9e75-ee2cf931db92', + compositionType: ContentTypeCompositionTypeModel.INHERITANCE, + }, + { + key: '8f68ba66-6fb2-4778-83b8-6ab4ca3a7c5d', + compositionType: ContentTypeCompositionTypeModel.COMPOSITION, + }, + ], + cleanup: { + preventCleanup: false, + keepAllVersionsNewerThanDays: null, + keepLatestVersionPerDayForDays: null, + }, + }, +]; + +export const treeData: Array = [ { name: 'Document Type 1', type: 'document-type', @@ -11,11 +710,7 @@ export const data: Array = [ key: 'd81c7957-153c-4b5a-aa6f-b434a4964624', isContainer: false, parentKey: null, - isFolder: false, - isElement: false, icon: '', - alias: 'documentType1', - properties: [], }, { name: 'Document Type 2', @@ -24,11 +719,7 @@ export const data: Array = [ key: 'a99e4018-3ffc-486b-aa76-eecea9593d17', isContainer: false, parentKey: null, - isFolder: false, - isElement: false, icon: '', - alias: 'documentType2', - properties: [], }, ]; @@ -36,23 +727,25 @@ export const data: Array = [ // TODO: all properties are optional in the server schema. I don't think this is correct. // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -class UmbDocumentTypeData extends UmbEntityData { +class UmbDocumentTypeData extends UmbEntityData { + private treeData = treeData; + constructor() { super(data); } getTreeRoot(): Array { - const rootItems = this.data.filter((item) => item.parentKey === null); + const rootItems = this.treeData.filter((item) => item.parentKey === null); return rootItems.map((item) => createDocumentTypeTreeItem(item)); } getTreeItemChildren(key: string): Array { - const childItems = this.data.filter((item) => item.parentKey === key); + const childItems = this.treeData.filter((item) => item.parentKey === key); return childItems.map((item) => createDocumentTypeTreeItem(item)); } getTreeItem(keys: Array): Array { - const items = this.data.filter((item) => keys.includes(item.key ?? '')); + const items = this.treeData.filter((item) => keys.includes(item.key ?? '')); return items.map((item) => createDocumentTypeTreeItem(item)); } } diff --git a/src/Umbraco.Web.UI.Client/src/core/mocks/data/document.data.ts b/src/Umbraco.Web.UI.Client/src/core/mocks/data/document.data.ts index 70a8507a3a..8eaf0ea55e 100644 --- a/src/Umbraco.Web.UI.Client/src/core/mocks/data/document.data.ts +++ b/src/Umbraco.Web.UI.Client/src/core/mocks/data/document.data.ts @@ -1,49 +1,15 @@ import { UmbEntityData } from './entity.data'; import { createDocumentTreeItem } from './utils'; -import { DocumentTreeItemModel, PagedDocumentTreeItemModel } from '@umbraco-cms/backend-api'; -import type { DocumentDetails } from '@umbraco-cms/models'; +import { + ContentStateModel, + DocumentModel, + DocumentTreeItemModel, + PagedDocumentTreeItemModel, +} from '@umbraco-cms/backend-api'; -export const data: Array = [ - { - name: 'Multiple Text String', - type: 'document', - icon: 'favorite', - hasChildren: false, - key: '6f31e382-458c-4f96-95ea-cc26c51009d4', - isContainer: false, - parentKey: null, - noAccess: false, - isProtected: false, - isPublished: false, - isEdited: false, - isTrashed: false, - properties: [ - { - alias: 'multipleTextString', - label: 'Multiple Text String', - description: '', - dataTypeKey: 'dt-multipleTextString', - }, - ], - data: [ - { - alias: 'multipleTextString', - value: [ - { - value: 'Value 1', - }, - { - value: 'Value 2', - }, - { - value: 'Value 3', - }, - ], - }, - ], - variants: [], - }, - { +/* + +{ name: 'All Property Editors', type: 'document', icon: 'favorite', @@ -247,179 +213,201 @@ export const data: Array = [ data: [], variants: [], }, + +*/ + +export const data: Array = [ { - name: 'Document 1', - type: 'document', - icon: 'document', - hasChildren: false, - key: '74e4008a-ea4f-4793-b924-15e02fd380d1', - isContainer: false, - parentKey: null, - noAccess: false, - isProtected: false, - isPublished: false, - isEdited: false, - isTrashed: false, + urls: [ + { + culture: 'en-US', + url: '/', + }, + ], + templateKey: null, + key: 'all-property-editors-document-key', + contentTypeKey: 'all-property-editors-document-type-key', properties: [ + // TODO: is 'properties' the correct name for this? The property comes from the doc type, and this only holds the values. { - alias: 'myHeadline', - label: 'Headline', - description: 'Text string property', - dataTypeKey: 'dt-textBox', - }, - { - alias: 'myDescription', - label: 'Description', - description: 'Textarea property', - dataTypeKey: 'dt-textArea', + culture: null, + segment: null, + alias: 'email', + value: 'mail@umbraco.com', }, ], - data: [ + variants: [ { - alias: 'myHeadline', - value: 'The daily life at Umbraco HQ', - }, - { - alias: 'myDescription', - value: 'Every day, a rabbit in a military costume greets me at the front door', + state: ContentStateModel.PUBLISHED, + publishDate: '2023-02-06T15:31:51.354764', + culture: 'en-us', + segment: null, + name: 'Blog post A', + createDate: '2023-02-06T15:31:46.876902', + updateDate: '2023-02-06T15:31:51.354764', }, ], - variants: [{ name: 'fake data' }], - /* - // Concept for node layout, separation of design from config and data. - layout: [ - { - type: 'group', - children: [ - { - type: 'property', - alias: 'myHeadline' - }, - { - type: 'property', - alias: 'myDescription' - } - ] - } - ], - */ }, { - name: 'Document 2', - type: 'document', - icon: 'favorite', - hasChildren: false, - key: '74e4008a-ea4f-4793-b924-15e02fd380d2', - isContainer: false, - parentKey: null, - noAccess: false, - isProtected: false, - isPublished: false, - isEdited: false, - isTrashed: false, + urls: [ + { + culture: 'en-US', + url: '/', + }, + ], + templateKey: null, + key: 'c05da24d-7740-447b-9cdc-bd8ce2172e38', + contentTypeKey: '29643452-cff9-47f2-98cd-7de4b6807681', properties: [ { - alias: 'myHeadline', - label: 'Text string label', - description: 'this is a text string property', - dataTypeKey: 'dt-textBox', + culture: null, + segment: null, + alias: 'masterText', + value: 'i have a master text', }, { - alias: 'myDescription', - label: 'Textarea label', - description: 'This is the a textarea property', - dataTypeKey: 'dt-textArea', + culture: null, + segment: null, + alias: 'pageTitle', + value: 'with a page title', }, { - alias: 'myExternalEditor', - label: 'My JS Property Editor', - description: 'This is the a external property', - dataTypeKey: 'dt-custom', + culture: null, + segment: null, + alias: 'blogPostText', + value: 'My first blog post', }, { - alias: 'myContentPicker', - label: 'Content Picker', - description: 'This is a content picker', - dataTypeKey: 'dt-contentPicker', + culture: 'en-us', + segment: null, + alias: 'blogTextStringUnderMasterTab', + value: 'in the master tab', + }, + { + culture: 'en-us', + segment: null, + alias: 'blogTextStringUnderGroupUnderMasterTab', + value: 'which is under another group in the tab', + }, + { + culture: null, + segment: null, + alias: 'localBlogTabString', + value: '1234567', }, ], - data: [ + variants: [ { - alias: 'myHeadline', - value: 'Is it all just fun and curling and scary rabbits?', - }, - { - alias: 'myDescription', - value: - "So no, there's not confetti every day. And no, there's not champagne every week or a crazy rabbit running around 🐰", - }, - { - alias: 'myExternalEditor', - value: 'Tex lkasdfkljdfsa 1', - }, - { - alias: 'myContextExampleEditor', - value: '', - }, - { - alias: 'myContentPicker', - value: '', + state: ContentStateModel.PUBLISHED, + publishDate: '2023-02-06T15:31:51.354764', + culture: 'en-us', + segment: null, + name: 'Blog post A', + createDate: '2023-02-06T15:31:46.876902', + updateDate: '2023-02-06T15:31:51.354764', }, ], - variants: [{ name: 'Variant 1' }], }, { - name: 'Document 3', + urls: [], + templateKey: null, + key: 'fd56a0b5-01a0-4da2-b428-52773bfa9cc4', + contentTypeKey: '29643452-cff9-47f2-98cd-7de4b6807681', + properties: [ + { + culture: null, + segment: null, + alias: 'masterText', + value: 'i have a master text B', + }, + { + culture: null, + segment: null, + alias: 'pageTitle', + value: 'with a page title B', + }, + { + culture: null, + segment: null, + alias: 'blogPostText', + value: 'My first blog post B', + }, + { + culture: 'en-us', + segment: null, + alias: 'blogTextStringUnderMasterTab', + value: 'in the master tab B', + }, + { + culture: 'en-us', + segment: null, + alias: 'blogTextStringUnderGroupUnderMasterTab', + value: 'which is under another group in the tab B', + }, + { + culture: null, + segment: null, + alias: 'localBlogTabString', + value: '1234567890', + }, + ], + variants: [ + { + state: ContentStateModel.DRAFT, + publishDate: '2023-02-06T15:32:24.957009', + culture: 'en-us', + segment: null, + name: 'Blog post B', + createDate: '2023-02-06T15:32:05.350038', + updateDate: '2023-02-06T15:32:24.957009', + }, + ], + }, +]; + +// TODO: make tree data: +export const treeData: Array = [ + { + isProtected: false, + isPublished: true, + isEdited: false, + noAccess: false, + isTrashed: false, + key: 'all-property-editors-document-key', + isContainer: false, + parentKey: null, + name: 'All property editors', type: 'document', - icon: 'document', + icon: 'icon-item-arrangement', + hasChildren: false, + }, + { + isProtected: false, + isPublished: true, + isEdited: false, + noAccess: false, + isTrashed: false, + key: 'c05da24d-7740-447b-9cdc-bd8ce2172e38', + isContainer: false, + parentKey: null, + name: 'Blog post A', + type: 'document', + icon: 'icon-item-arrangement', hasChildren: true, - key: 'cdd30288-2d1c-41b4-89a9-61647b4a10d5', - isContainer: false, - parentKey: null, - noAccess: false, - isProtected: false, - isPublished: false, - isEdited: false, - isTrashed: false, - properties: [ - { - alias: 'myDescription', - label: 'Description', - description: 'Textarea property', - dataTypeKey: 'dt-textArea', - }, - ], - data: [ - { - alias: 'myDescription', - value: 'Every day, a rabbit in a military costume greets me at the front door', - }, - ], - variants: [], }, { - name: 'Document 4', - type: 'document', - icon: 'document', - hasChildren: false, - key: 'f6f7a5b2-e7c0-463a-97bc-6cb5b9bcf447', - isContainer: false, - parentKey: 'cdd30288-2d1c-41b4-89a9-61647b4a10d5', - noAccess: false, isProtected: false, isPublished: false, isEdited: false, + noAccess: false, isTrashed: false, - properties: [ - { - alias: 'myDescription', - label: 'Description', - description: 'Textarea property', - dataTypeKey: 'dt-textArea', - }, - ], - data: [], - variants: [], + key: 'fd56a0b5-01a0-4da2-b428-52773bfa9cc4', + isContainer: false, + parentKey: 'c05da24d-7740-447b-9cdc-bd8ce2172e38', + name: 'Blog post B', + type: 'document', + icon: 'icon-item-arrangement', + hasChildren: false, }, { name: 'Document 5', @@ -434,9 +422,6 @@ export const data: Array = [ isPublished: false, isEdited: false, isTrashed: false, - properties: [], - data: [], - variants: [], }, ]; @@ -444,27 +429,29 @@ export const data: Array = [ // TODO: all properties are optional in the server schema. I don't think this is correct. // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -class UmbDocumentData extends UmbEntityData { +class UmbDocumentData extends UmbEntityData { + private treeData = treeData; + constructor() { super(data); } getTreeRoot(): PagedDocumentTreeItemModel { - const items = this.data.filter((item) => item.parentKey === null); + const items = this.treeData.filter((item) => item.parentKey === null); const treeItems = items.map((item) => createDocumentTreeItem(item)); const total = items.length; return { items: treeItems, total }; } getTreeItemChildren(key: string): PagedDocumentTreeItemModel { - const items = this.data.filter((item) => item.parentKey === key); + const items = this.treeData.filter((item) => item.parentKey === key); const treeItems = items.map((item) => createDocumentTreeItem(item)); const total = items.length; return { items: treeItems, total }; } getTreeItem(keys: Array): Array { - const items = this.data.filter((item) => keys.includes(item.key ?? '')); + const items = this.treeData.filter((item) => keys.includes(item.key ?? '')); return items.map((item) => createDocumentTreeItem(item)); } } diff --git a/src/Umbraco.Web.UI.Client/src/core/mocks/data/utils.ts b/src/Umbraco.Web.UI.Client/src/core/mocks/data/utils.ts index 3a03bf1ce0..5465a4d385 100644 --- a/src/Umbraco.Web.UI.Client/src/core/mocks/data/utils.ts +++ b/src/Umbraco.Web.UI.Client/src/core/mocks/data/utils.ts @@ -4,8 +4,9 @@ import type { DocumentTypeTreeItemModel, EntityTreeItemModel, FolderTreeItemModel, + DocumentTypeModel, + DocumentModel, } from '@umbraco-cms/backend-api'; -import type { DocumentDetails, DocumentTypeDetails } from '@umbraco-cms/models'; export const createEntityTreeItem = (item: any): EntityTreeItemModel => { return { @@ -36,18 +37,20 @@ export const createContentTreeItem = (item: any): ContentTreeItemModel & { isTra }; // TODO: remove isTrashed type extension when we have found a solution to trashed items -export const createDocumentTreeItem = (item: DocumentDetails): DocumentTreeItemModel & { isTrashed: boolean } => { +export const createDocumentTreeItem = (item: DocumentModel): DocumentTreeItemModel & { isTrashed: boolean } => { return { ...createContentTreeItem(item), + /* noAccess: item.noAccess, isProtected: item.isProtected, isPublished: item.isPublished, isEdited: item.isEdited, isTrashed: item.isTrashed, + */ }; }; -export const createDocumentTypeTreeItem = (item: DocumentTypeDetails): DocumentTypeTreeItemModel => { +export const createDocumentTypeTreeItem = (item: DocumentTypeModel): DocumentTypeTreeItemModel => { return { ...createFolderTreeItem(item), isElement: item.isElement, diff --git a/src/Umbraco.Web.UI.Client/src/core/mocks/domains/data-type.handlers.ts b/src/Umbraco.Web.UI.Client/src/core/mocks/domains/data-type.handlers.ts index 3351ca071c..a7aea08797 100644 --- a/src/Umbraco.Web.UI.Client/src/core/mocks/domains/data-type.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/core/mocks/domains/data-type.handlers.ts @@ -1,32 +1,14 @@ import { rest } from 'msw'; import { umbDataTypeData } from '../data/data-type.data'; -import type { DataTypeDetails } from '@umbraco-cms/models'; +import { umbracoPath } from '@umbraco-cms/utils'; // TODO: add schema export const handlers = [ - rest.get('/umbraco/backoffice/data-type/details/:key', (req, res, ctx) => { + rest.delete('/umbraco/backoffice/data-type/:key', async (req, res, ctx) => { const key = req.params.key as string; if (!key) return; - const dataType = umbDataTypeData.getByKey(key); - - return res(ctx.status(200), ctx.json([dataType])); - }), - - rest.post('/umbraco/backoffice/data-type/save', async (req, res, ctx) => { - const data = await req.json(); - if (!data) return; - - const saved = umbDataTypeData.save(data); - - return res(ctx.status(200), ctx.json(saved)); - }), - - rest.post('/umbraco/backoffice/data-type/delete', async (req, res, ctx) => { - console.warn('Please move to schema'); - const keys = await req.json(); - - umbDataTypeData.delete(keys); + umbDataTypeData.delete([key]); return res(ctx.status(200)); }), @@ -60,4 +42,31 @@ export const handlers = [ const items = umbDataTypeData.getTreeItem(keys); return res(ctx.status(200), ctx.json(items)); }), + + rest.get(umbracoPath('/data-type/:key'), (req, res, ctx) => { + const key = req.params.key as string; + if (!key) return; + + const dataType = umbDataTypeData.getByKey(key); + + return res(ctx.status(200), ctx.json(dataType)); + }), + + rest.post(umbracoPath('/data-type/:key'), async (req, res, ctx) => { + const data = await req.json(); + if (!data) return; + + const saved = umbDataTypeData.save(data); + + return res(ctx.status(200), ctx.json(saved)); + }), + + rest.put(umbracoPath('/data-type/:key'), async (req, res, ctx) => { + const data = await req.json(); + if (!data) return; + + const saved = umbDataTypeData.save(data); + + return res(ctx.status(200), ctx.json(saved)); + }), ]; diff --git a/src/Umbraco.Web.UI.Client/src/core/mocks/domains/document-type.handlers.ts b/src/Umbraco.Web.UI.Client/src/core/mocks/domains/document-type.handlers.ts index 5478effc6a..67be0d6bbf 100644 --- a/src/Umbraco.Web.UI.Client/src/core/mocks/domains/document-type.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/core/mocks/domains/document-type.handlers.ts @@ -1,19 +1,11 @@ import { rest } from 'msw'; import { umbDocumentTypeData } from '../data/document-type.data'; -import type { DocumentTypeDetails } from '@umbraco-cms/models'; +import type { DocumentTypeModel } from '@umbraco-cms/backend-api'; +import { umbracoPath } from '@umbraco-cms/utils'; // TODO: add schema export const handlers = [ - rest.get('/umbraco/backoffice/document-type/:key', (req, res, ctx) => { - const key = req.params.key as string; - if (!key) return; - - const document = umbDocumentTypeData.getByKey(key); - - return res(ctx.status(200), ctx.json([document])); - }), - - rest.post('/umbraco/backoffice/document-type/save', (req, res, ctx) => { + rest.post('/umbraco/backoffice/document-type/save', (req, res, ctx) => { const data = req.body; if (!data) return; @@ -53,4 +45,13 @@ export const handlers = [ return res(ctx.status(200), ctx.json(items)); }), + + rest.get(umbracoPath('/document-type/:key'), (req, res, ctx) => { + const key = req.params.key as string; + if (!key) return; + + const document = umbDocumentTypeData.getByKey(key); + + return res(ctx.status(200), ctx.json(document)); + }), ]; diff --git a/src/Umbraco.Web.UI.Client/src/core/mocks/domains/document.handlers.ts b/src/Umbraco.Web.UI.Client/src/core/mocks/domains/document.handlers.ts index d32229ad61..560b50131e 100644 --- a/src/Umbraco.Web.UI.Client/src/core/mocks/domains/document.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/core/mocks/domains/document.handlers.ts @@ -1,29 +1,10 @@ import { rest } from 'msw'; import { umbDocumentData } from '../data/document.data'; -import type { DocumentDetails } from '@umbraco-cms/models'; +import type { DocumentModel } from '@umbraco-cms/backend-api'; +import { umbracoPath } from '@umbraco-cms/utils'; // TODO: add schema export const handlers = [ - rest.get('/umbraco/management/api/v1/document/details/:key', (req, res, ctx) => { - console.warn('Please move to schema'); - const key = req.params.key as string; - if (!key) return; - - const document = umbDocumentData.getByKey(key); - - return res(ctx.status(200), ctx.json([document])); - }), - - rest.post('/umbraco/management/api/v1/document/save', async (req, res, ctx) => { - console.warn('Please move to schema'); - const data = await req.json(); - if (!data) return; - - const saved = umbDocumentData.save(data); - - return res(ctx.status(200), ctx.json(saved)); - }), - rest.post('/umbraco/management/api/v1/document/trash', async (req, res, ctx) => { console.warn('Please move to schema'); const keys = await req.json(); @@ -53,4 +34,22 @@ export const handlers = [ return res(ctx.status(200), ctx.json(items)); }), + + rest.post('/umbraco/management/api/v1/document/:key', async (req, res, ctx) => { + const data = await req.json(); + if (!data) return; + + const saved = umbDocumentData.save(data); + + return res(ctx.status(200), ctx.json(saved)); + }), + + rest.get(umbracoPath('/document/:key'), (req, res, ctx) => { + const key = req.params.key as string; + if (!key) return; + + const document = umbDocumentData.getByKey(key); + + return res(ctx.status(200), ctx.json(document)); + }), ]; diff --git a/src/Umbraco.Web.UI.Client/src/core/mocks/domains/template.handlers.ts b/src/Umbraco.Web.UI.Client/src/core/mocks/domains/template.handlers.ts index bfc95c1594..6b159c625e 100644 --- a/src/Umbraco.Web.UI.Client/src/core/mocks/domains/template.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/core/mocks/domains/template.handlers.ts @@ -1,6 +1,6 @@ import { rest } from 'msw'; -import { umbracoPath } from '@umbraco-cms/utils'; import { umbTemplateData } from '../data/template.data'; +import { umbracoPath } from '@umbraco-cms/utils'; import { TemplateCreateModel, TemplateUpdateModel } from '@umbraco-cms/backend-api'; // TODO: add schema