diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index ec4382fd9d..cebf52b865 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -83,6 +83,7 @@ "./user": "./dist-cms/packages/user/user/index.js", "./utils": "./dist-cms/packages/core/utils/index.js", "./variant": "./dist-cms/packages/core/variant/index.js", + "./webhook": "./dist-cms/packages/webhook/index.js", "./workspace": "./dist-cms/packages/core/workspace/index.js", "./external/backend-api": "./dist-cms/external/backend-api/index.js", "./external/dompurify": "./dist-cms/external/dompurify/index.js", diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts index 567d7e254c..923ebbc225 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts @@ -36,6 +36,7 @@ const CORE_PACKAGES = [ import('../../packages/tiny-mce/umbraco-package.js'), import('../../packages/umbraco-news/umbraco-package.js'), import('../../packages/user/umbraco-package.js'), + import('../../packages/webhook/umbraco-package.js'), ]; @customElement('umb-backoffice') diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts index 59691293a3..01f5e94555 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/da-dk.ts @@ -1790,6 +1790,7 @@ export default { settingsGroup: 'Indstillinger', templatingGroup: 'Design og layout', thirdPartyGroup: 'Tredjepart', + webhooks: 'Webhooks', }, update: { updateAvailable: 'Ny opdatering er klar', diff --git a/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts b/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts index e4c941f1ac..b292ea7099 100644 --- a/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts +++ b/src/Umbraco.Web.UI.Client/src/assets/lang/en-us.ts @@ -1782,6 +1782,7 @@ export default { settingsGroup: 'Settings', templatingGroup: 'Templating', thirdPartyGroup: 'Third Party', + webhooks: 'Webhooks', }, update: { updateAvailable: 'New update ready', diff --git a/src/Umbraco.Web.UI.Client/src/mocks/browser-handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/browser-handlers.ts index 67b6ae8118..161d6e9823 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/browser-handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/browser-handlers.ts @@ -11,6 +11,7 @@ import { handlers as healthCheckHandlers } from './handlers/health-check.handler import { handlers as installHandlers } from './handlers/install.handlers.js'; import { handlers as languageHandlers } from './handlers/language/index.js'; import { handlers as logViewerHandlers } from './handlers/log-viewer.handlers.js'; +import { handlers as webhookHandlers } from './handlers/webhook.handlers.js'; import { handlers as mediaHandlers } from './handlers/media/index.js'; import { handlers as mediaTypeHandlers } from './handlers/media-type/index.js'; import { handlers as memberGroupHandlers } from './handlers/member-group/index.js'; @@ -52,6 +53,7 @@ const handlers = [ ...installHandlers, ...languageHandlers, ...logViewerHandlers, + ...webhookHandlers, ...mediaHandlers, ...mediaTypeHandlers, ...memberGroupHandlers, diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/webhooks.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/webhooks.data.ts new file mode 100644 index 0000000000..9685f3612e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/webhooks.data.ts @@ -0,0 +1,14 @@ +import { UmbMockDBBase } from './utils/mock-db-base.js'; +//import { +// WebhooksResponseModel, +//} from '@umbraco-cms/backoffice/backend-api'; + +class UmbWebhooksData extends UmbMockDBBase { + constructor(data: any[]) { + super(data); + } +} + +export const umbWebhooksData = { + //webhooks: new UmbWebhooksData(webhooks), +}; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/log-viewer.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/log-viewer.handlers.ts index 7ed17a52f7..c1aabd575f 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/log-viewer.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/log-viewer.handlers.ts @@ -40,7 +40,7 @@ export const handlers = [ }), //#endregion - //#region Temaplates + //#region Templates rest.get(umbracoPath('/log-viewer/message-template'), (req, res, ctx) => { const skip = req.url.searchParams.get('skip'); const skipNumber = skip ? Number.parseInt(skip) : undefined; @@ -57,6 +57,7 @@ export const handlers = [ return res(ctx.delay(), ctx.status(200), ctx.json(response)); }), //#endregion + //#region Logs rest.get(umbracoPath('/log-viewer/level'), (req, res, ctx) => { return res(ctx.delay(), ctx.status(200), ctx.json(umbLogViewerData.logLevels)); diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/webhook.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/webhook.handlers.ts new file mode 100644 index 0000000000..cfc6005826 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/webhook.handlers.ts @@ -0,0 +1,6 @@ +const { rest } = window.MockServiceWorker; +//import { umbracoPath } from '@umbraco-cms/backoffice/utils'; + +export const handlers = [ + +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer/logviewer-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer/logviewer-workspace.element.ts index d817b96bcc..c0aed6bc80 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer/logviewer-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer/logviewer-workspace.element.ts @@ -33,10 +33,7 @@ export class UmbLogViewerWorkspaceElement extends UmbLitElement { } render() { - return html` - - - `; + return html` `; } static styles = [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/action/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/action/manifests.ts new file mode 100644 index 0000000000..bfaabbb79a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/action/manifests.ts @@ -0,0 +1,22 @@ +import { UMB_COLLECTION_ALIAS_CONDITION } from '@umbraco-cms/backoffice/collection'; +import type { ManifestCollectionAction } from '@umbraco-cms/backoffice/extension-registry'; + +export const createManifest: ManifestCollectionAction = { + type: 'collectionAction', + kind: 'button', + name: 'Create Webhook Collection Action', + alias: 'Umb.CollectionAction.Webhook.Create', + weight: 200, + meta: { + label: 'Create', + href: 'section/settings/workspace/webhook/create', + }, + conditions: [ + { + alias: UMB_COLLECTION_ALIAS_CONDITION, + match: 'Umb.Collection.Webhook', + }, + ], +}; + +export const manifests = [createManifest]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/index.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/index.ts new file mode 100644 index 0000000000..6ac354feaf --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/index.ts @@ -0,0 +1,2 @@ +export { UmbWebhookCollectionRepository } from './repository/index.js'; +export { UMB_WEBHOOK_COLLECTION_ALIAS } from './manifests.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/manifests.ts new file mode 100644 index 0000000000..913d773738 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/manifests.ts @@ -0,0 +1,26 @@ +import { + manifests as collectionRepositoryManifests, + UMB_WEBHOOK_COLLECTION_REPOSITORY_ALIAS, +} from './repository/manifests.js'; +import { manifests as collectionViewManifests } from './views/manifests.js'; +import { manifests as collectionActionManifests } from './action/manifests.js'; +import type { ManifestCollection } from '@umbraco-cms/backoffice/extension-registry'; + +export const UMB_WEBHOOK_COLLECTION_ALIAS = 'Umb.Collection.Webhook'; + +const collectionManifest: ManifestCollection = { + type: 'collection', + kind: 'default', + alias: UMB_WEBHOOK_COLLECTION_ALIAS, + name: 'Webhook Collection', + meta: { + repositoryAlias: UMB_WEBHOOK_COLLECTION_REPOSITORY_ALIAS, + }, +}; + +export const manifests = [ + collectionManifest, + ...collectionRepositoryManifests, + ...collectionViewManifests, + ...collectionActionManifests, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/repository/index.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/repository/index.ts new file mode 100644 index 0000000000..aa27b1259b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/repository/index.ts @@ -0,0 +1,2 @@ +export { UMB_WEBHOOK_COLLECTION_REPOSITORY_ALIAS } from './manifests.js'; +export { UmbWebhookCollectionRepository } from './webhook-collection.repository.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/repository/manifests.ts new file mode 100644 index 0000000000..6295051cb7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/repository/manifests.ts @@ -0,0 +1,12 @@ +import type { ManifestRepository } from '@umbraco-cms/backoffice/extension-registry'; + +export const UMB_WEBHOOK_COLLECTION_REPOSITORY_ALIAS = 'Umb.Repository.WebhookCollection'; + +const repository: ManifestRepository = { + type: 'repository', + alias: UMB_WEBHOOK_COLLECTION_REPOSITORY_ALIAS, + name: 'Webhook Collection Repository', + api: () => import('./webhook-collection.repository.js'), +}; + +export const manifests = [repository]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/repository/types.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/repository/types.ts new file mode 100644 index 0000000000..28989e140e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/repository/types.ts @@ -0,0 +1,8 @@ +import type { UmbWebhookDetailModel } from '../../types.js'; +import type { UmbWebhookCollectionFilterModel } from '../types.js'; +import type { UmbCollectionDataSource } from '@umbraco-cms/backoffice/collection'; + +export type UmbWebhookCollectionDataSource = UmbCollectionDataSource< + UmbWebhookDetailModel, + UmbWebhookCollectionFilterModel +>; diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/repository/webhook-collection.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/repository/webhook-collection.repository.ts new file mode 100644 index 0000000000..96a19f53ca --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/repository/webhook-collection.repository.ts @@ -0,0 +1,21 @@ +import type { UmbWebhookCollectionFilterModel } from '../types.js'; +import { UmbWebhookCollectionServerDataSource } from './webhook-collection.server.data-source.js'; +import type { UmbWebhookCollectionDataSource } from './types.js'; +import type { UmbCollectionRepository } from '@umbraco-cms/backoffice/collection'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +export class UmbWebhookCollectionRepository implements UmbCollectionRepository { + #collectionSource: UmbWebhookCollectionDataSource; + + constructor(host: UmbControllerHost) { + this.#collectionSource = new UmbWebhookCollectionServerDataSource(host); + } + + async requestCollection(filter: UmbWebhookCollectionFilterModel) { + return this.#collectionSource.getCollection(filter); + } + + destroy(): void {} +} + +export default UmbWebhookCollectionRepository; diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/repository/webhook-collection.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/repository/webhook-collection.server.data-source.ts new file mode 100644 index 0000000000..2a59759ad9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/repository/webhook-collection.server.data-source.ts @@ -0,0 +1,55 @@ +import type { UmbWebhookCollectionFilterModel } from '../types.js'; +import type { UmbWebhookDetailModel } from '../../types.js'; +import { UMB_WEBHOOK_ENTITY_TYPE } from '../../entity.js'; +import { WebhookResource } from '@umbraco-cms/backoffice/external/backend-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; + +/** + * A data source that fetches the webhook collection data from the server. + * @export + * @class UmbWebhookCollectionServerDataSource + * @implements {UmbCollectionDataSource} + */ +export class UmbWebhookCollectionServerDataSource implements UmbWebhookCollectionServerDataSource { + #host: UmbControllerHost; + + /** + * Creates an instance of UmbWebhookCollectionServerDataSource. + * @param {UmbControllerHost} host + * @memberof UmbWebhookCollectionServerDataSource + */ + constructor(host: UmbControllerHost) { + this.#host = host; + } + + /** + * Gets the Wwbhook collection filtered by the given filter. + * @param {UmbWebhookCollectionFilterModel} filter + * @return {*} + * @memberof UmbWebhookCollectionServerDataSource + */ + async getCollection(_filter: UmbWebhookCollectionFilterModel) { + const { data, error } = await tryExecuteAndNotify(this.#host, WebhookResource.getWebhookItem({})); + + if (data) { + const items = data.map((item) => { + const model: UmbWebhookDetailModel = { + entityType: UMB_WEBHOOK_ENTITY_TYPE, + unique: item.url, + name: item.name, + url: item.url, + enabled: item.enabled, + events: item.events.split(','), + types: item.types.split(','), + }; + + return model; + }); + + return { data: { items, total: data.length } }; + } + + return { error }; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/types.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/types.ts new file mode 100644 index 0000000000..fc7ef64ca7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/types.ts @@ -0,0 +1,4 @@ +export interface UmbWebhookCollectionFilterModel { + skip?: number; + take?: number; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/views/index.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/views/index.ts new file mode 100644 index 0000000000..31e3d80739 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/views/index.ts @@ -0,0 +1 @@ +export { UMB_WEBHOOK_TABLE_COLLECTION_VIEW_ALIAS } from './manifests.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/views/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/views/manifests.ts new file mode 100644 index 0000000000..2eed3f5374 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/views/manifests.ts @@ -0,0 +1,24 @@ +import { UMB_COLLECTION_ALIAS_CONDITION } from '@umbraco-cms/backoffice/collection'; +import type { ManifestCollectionView } from '@umbraco-cms/backoffice/extension-registry'; + +export const UMB_WEBHOOK_TABLE_COLLECTION_VIEW_ALIAS = 'Umb.CollectionView.Webhook.Table'; + +const tableCollectionView: ManifestCollectionView = { + type: 'collectionView', + alias: UMB_WEBHOOK_TABLE_COLLECTION_VIEW_ALIAS, + name: 'Webhook Table Collection View', + js: () => import('./table/webhook-table-collection-view.element.js'), + meta: { + label: 'Table', + icon: 'icon-list', + pathName: 'table', + }, + conditions: [ + { + alias: UMB_COLLECTION_ALIAS_CONDITION, + match: 'Umb.Collection.Webhook', + }, + ], +}; + +export const manifests = [tableCollectionView]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/views/table/column-layouts/boolean/webhook-table-boolean-column-layout.element.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/views/table/column-layouts/boolean/webhook-table-boolean-column-layout.element.ts new file mode 100644 index 0000000000..c992d32667 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/views/table/column-layouts/boolean/webhook-table-boolean-column-layout.element.ts @@ -0,0 +1,18 @@ +import { html, nothing, customElement, property } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; + +@customElement('umb-webhook-table-boolean-column-layout') +export class UmbWebhookTableBooleanColumnLayoutElement extends UmbLitElement { + @property({ attribute: false }) + value = false; + + render() { + return this.value ? html`` : nothing; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'umb-webhook-table-boolean-column-layout': UmbWebhookTableBooleanColumnLayoutElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/views/table/column-layouts/entity-actions/webhook-table-entity-actions-column-layout.element.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/views/table/column-layouts/entity-actions/webhook-table-entity-actions-column-layout.element.ts new file mode 100644 index 0000000000..7fc4561d65 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/views/table/column-layouts/entity-actions/webhook-table-entity-actions-column-layout.element.ts @@ -0,0 +1,34 @@ +import type { UmbWebhookDetailModel } from '../../../../../types.js'; +import { html, customElement, property, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; + +@customElement('umb-webhook-table-entity-actions-column-layout') +export class UmbWebhookTableEntityActionsColumnLayoutElement extends UmbLitElement { + @property({ attribute: false }) + value!: UmbWebhookDetailModel; + + @state() + _isOpen = false; + + #onActionExecuted() { + this._isOpen = false; + } + + render() { + return html` + + + + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'umb-webhook-table-entity-actions-column-layout': UmbWebhookTableEntityActionsColumnLayoutElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/views/table/column-layouts/name/webhook-table-name-column-layout.element.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/views/table/column-layouts/name/webhook-table-name-column-layout.element.ts new file mode 100644 index 0000000000..f90515ddac --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/views/table/column-layouts/name/webhook-table-name-column-layout.element.ts @@ -0,0 +1,21 @@ +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { html, LitElement, nothing, customElement, property } from '@umbraco-cms/backoffice/external/lit'; + +@customElement('umb-w-table-name-column-layout') +export class UmbLanguageTableNameColumnLayoutElement extends LitElement { + @property({ attribute: false }) + value!: { unique: string; name: string }; + + render() { + if (!this.value) return nothing; + return html`${this.value.name}`; + } + + static styles = [UmbTextStyles]; +} + +declare global { + interface HTMLElementTagNameMap { + 'umb-language-table-name-column-layout': UmbLanguageTableNameColumnLayoutElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/views/table/webhook-table-collection-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/views/table/webhook-table-collection-view.element.ts new file mode 100644 index 0000000000..c70a6e2e80 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/collection/views/table/webhook-table-collection-view.element.ts @@ -0,0 +1,119 @@ +import type { UmbWebhookDetailModel } from '../../../types.js'; +import type { UmbDefaultCollectionContext } from '@umbraco-cms/backoffice/collection'; +import { UMB_DEFAULT_COLLECTION_CONTEXT } from '@umbraco-cms/backoffice/collection'; +import type { UmbTableColumn, UmbTableConfig, UmbTableItem } from '@umbraco-cms/backoffice/components'; +import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; + +import './column-layouts/boolean/webhook-table-boolean-column-layout.element.js'; +import './column-layouts/name/webhook-table-name-column-layout.element.js'; +import './column-layouts/entity-actions/webhook-table-entity-actions-column-layout.element.js'; + +@customElement('umb-webhook-table-collection-view') +export class UmbWebhookTableCollectionViewElement extends UmbLitElement { + @state() + private _tableConfig: UmbTableConfig = { + allowSelection: false, + }; + + @state() + private _tableColumns: Array = [ + { + name: 'Enabled', + alias: 'enabled', + elementName: 'umb-webhook-table-boolean-column-layout', + }, + { + name: 'URL', + alias: 'url', + }, + { + name: 'Events', + alias: 'events', + }, + { + name: 'Types', + alias: 'types', + }, + { + name: '', + alias: 'entityActions', + elementName: 'umb-webhook-table-entity-actions-column-layout', + }, + ]; + + @state() + private _tableItems: Array = []; + + #collectionContext?: UmbDefaultCollectionContext; + + constructor() { + super(); + + this.consumeContext(UMB_DEFAULT_COLLECTION_CONTEXT, (instance) => { + this.#collectionContext = instance; + this.#observeCollectionItems(); + }); + } + + #observeCollectionItems() { + if (!this.#collectionContext) return; + this.observe(this.#collectionContext.items, (items) => this.#createTableItems(items), 'umbCollectionItemsObserver'); + } + + #createTableItems(webhooks: Array) { + this._tableItems = webhooks.map((webhook) => { + return { + id: webhook.unique, + icon: 'icon-webhook', + data: [ + { + columnAlias: 'enabled', + value: webhook.enabled, + }, + { + columnAlias: 'url', + value: webhook.url, + }, + { + columnAlias: 'events', + value: webhook.events, + }, + { + columnAlias: 'types', + value: webhook.types, + }, + { + columnAlias: 'entityActions', + value: webhook, + }, + ], + }; + }); + } + + render() { + return html` + + `; + } + + static styles = [ + UmbTextStyles, + css` + :host { + display: flex; + flex-direction: column; + } + `, + ]; +} + +export default UmbWebhookTableCollectionViewElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-webhook-table-collection-view': UmbWebhookTableCollectionViewElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/entity.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/entity.ts new file mode 100644 index 0000000000..f78d477122 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/entity.ts @@ -0,0 +1,4 @@ +export const UMB_WEBHOOK_ENTITY_TYPE = 'webhook'; +export const UMB_WEBHOOK_WORKSPACE = 'Umb.Workspace.Webhook'; + +export type UmbWebhookEntityType = typeof UMB_WEBHOOK_ENTITY_TYPE; diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/index.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/index.ts new file mode 100644 index 0000000000..85eed50a10 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/index.ts @@ -0,0 +1,6 @@ +import './components/index.js'; + +export * from './collection/index.js'; +export * from './workspace/index.js'; + +export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/manifests.ts new file mode 100644 index 0000000000..f5ebb840c6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/manifests.ts @@ -0,0 +1,5 @@ +import { manifests as treeManifests } from './menu-item/manifests.js'; +import { manifests as workspaceManifests } from './workspace/manifests.js'; +import { manifests as collectionManifests } from './collection/manifests.js'; + +export const manifests = [...treeManifests, ...workspaceManifests, ...collectionManifests]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/menu-item/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/menu-item/manifests.ts new file mode 100644 index 0000000000..cba613ace3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/menu-item/manifests.ts @@ -0,0 +1,17 @@ +import { UMB_WEBHOOK_ENTITY_TYPE } from '../entity.js'; +import type { ManifestMenuItem } from '@umbraco-cms/backoffice/extension-registry'; + +const menuItem: ManifestMenuItem = { + type: 'menuItem', + alias: 'Umb.MenuItem.Webhook', + name: 'Webhook Menu Item', + weight: 100, + meta: { + label: 'Webhooks', + icon: 'icon-webhook', + entityType: UMB_WEBHOOK_ENTITY_TYPE, + menus: ['Umb.Menu.Settings'], + }, +}; + +export const manifests = [menuItem]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/types.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/types.ts new file mode 100644 index 0000000000..aba4dc31ba --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/types.ts @@ -0,0 +1,11 @@ +import type { UmbWebhookEntityType } from './entity.js'; + +export interface UmbWebhookDetailModel { + entityType: UmbWebhookEntityType; + unique: string; + name: string; + enabled: boolean; + url: string; + events: string[] | null; + types: string[] | null; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/umbraco-package.ts new file mode 100644 index 0000000000..a5c24dd8b6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/umbraco-package.ts @@ -0,0 +1,9 @@ +export const name = 'Umbraco.Core.Webhook'; +export const extensions = [ + { + name: 'Webhook Bundle', + alias: 'Umb.Bundle.Webhook', + type: 'bundle', + js: () => import('./manifests.js'), + }, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/index.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/index.ts new file mode 100644 index 0000000000..897634ac15 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/index.ts @@ -0,0 +1 @@ +export * from './webhook.context.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/manifests.ts new file mode 100644 index 0000000000..64e980a76e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/manifests.ts @@ -0,0 +1,3 @@ +import { manifests as webhookManifests } from './webhook/manifests.js'; + +export const manifests = [...webhookManifests]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/views/overview/webhook-overview-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/views/overview/webhook-overview-view.element.ts new file mode 100644 index 0000000000..431370de55 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/views/overview/webhook-overview-view.element.ts @@ -0,0 +1,18 @@ +import { UMB_WEBHOOK_COLLECTION_ALIAS } from '../../../collection/index.js'; +import { html, customElement } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; + +@customElement('umb-webook-root-workspace') +export class UmbWebhookRootWorkspaceElement extends UmbLitElement { + render() { + return html` `; + } +} + +export default UmbWebhookRootWorkspaceElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-webhook-root-workspace': UmbWebhookRootWorkspaceElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/webhook.context.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/webhook.context.ts new file mode 100644 index 0000000000..8172f2a6f7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/webhook.context.ts @@ -0,0 +1,27 @@ +import { UMB_WEBHOOK_ENTITY_TYPE, UMB_WEBHOOK_WORKSPACE } from '../entity.js'; +import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; +import type { UmbWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; + +export class UmbWebhookWorkspaceContext extends UmbControllerBase implements UmbWorkspaceContextInterface { + public readonly workspaceAlias = UMB_WEBHOOK_WORKSPACE; + + getEntityType() { + return UMB_WEBHOOK_ENTITY_TYPE; + } + + getUnique() { + return undefined; + } + + constructor(host: UmbControllerHostElement) { + super(host); + this.provideContext(UMB_WORKSPACE_CONTEXT, this); + // TODO: Revisit usage of workspace for this case... currently no other workspace context provides them self with their own token. + this.provideContext(UMB_APP_WEBHOOK_CONTEXT, this); + } +} + +export const UMB_APP_WEBHOOK_CONTEXT = new UmbContextToken(UmbWebhookWorkspaceContext.name); diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/webhook/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/webhook/manifests.ts new file mode 100644 index 0000000000..164e767189 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/webhook/manifests.ts @@ -0,0 +1,53 @@ +import { UMB_WEBHOOK_ENTITY_TYPE, UMB_WEBHOOK_WORKSPACE } from '../../entity.js'; +import type { ManifestWorkspace, ManifestWorkspaceView } from '@umbraco-cms/backoffice/extension-registry'; + +const workspace: ManifestWorkspace = { + type: 'workspace', + alias: UMB_WEBHOOK_WORKSPACE, + name: 'Webhook Root Workspace', + js: () => import('./webhook-workspace.element.js'), + meta: { + entityType: UMB_WEBHOOK_ENTITY_TYPE, + }, +}; + +const workspaceViews: Array = [ + { + type: 'workspaceView', + alias: 'Umb.WorkspaceView.Webhook.Overview', + name: 'Webhook Root Workspace Overview View', + js: () => import('../views/overview/webhook-overview-view.element.js'), + weight: 300, + meta: { + label: 'Overview', + pathname: 'overview', + icon: 'icon-webhook', + }, + conditions: [ + { + alias: 'Umb.Condition.WorkspaceAlias', + match: workspace.alias, + }, + ], + }, + { + type: 'workspaceView', + alias: 'Umb.WorkspaceView.Webhook.Search', + name: 'Webhook Root Workspace Logs View', + js: () => import('../views/overview/webhook-overview-view.element.js'), + weight: 200, + meta: { + label: 'Logs', + pathname: 'logs', + icon: 'icon-box-alt', + }, + conditions: [ + { + alias: 'Umb.Condition.WorkspaceAlias', + match: workspace.alias, + }, + ], + }, +]; + +export const manifests = [workspace, ...workspaceViews]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/webhook/webhook-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/webhook/webhook-workspace.element.ts new file mode 100644 index 0000000000..53679499cd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/webhook/webhook-workspace.element.ts @@ -0,0 +1,27 @@ +import { UmbWebhookWorkspaceContext } from '../webhook.context.js'; +import { html, customElement } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; + +@customElement('umb-webhook-workspace') +export class UmbWebhookWorkspaceElement extends UmbLitElement { + /** + * The context for the webhook workspace. + * This is used to provide the workspace with the necessary data and services even though it does not have any references. + */ + #webhookWorkspaceContext = new UmbWebhookWorkspaceContext(this); + + render() { + return html` + `; + } +} + +export default UmbWebhookWorkspaceElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-webhook-workspace': UmbWebhookWorkspaceElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/tsconfig.json b/src/Umbraco.Web.UI.Client/tsconfig.json index d685b5fa3d..2a82dbb81e 100644 --- a/src/Umbraco.Web.UI.Client/tsconfig.json +++ b/src/Umbraco.Web.UI.Client/tsconfig.json @@ -101,6 +101,7 @@ "@umbraco-cms/backoffice/user": ["./src/packages/user/user/index.ts"], "@umbraco-cms/backoffice/utils": ["./src/packages/core/utils/index.ts"], "@umbraco-cms/backoffice/variant": ["./src/packages/core/variant/index.ts"], + "@umbraco-cms/backoffice/webhook": ["./src/packages/webhook/index.ts"], "@umbraco-cms/backoffice/workspace": ["./src/packages/core/workspace/index.ts"], "@umbraco-cms/backoffice/external/backend-api": ["./src/external/backend-api/index.ts"], "@umbraco-cms/backoffice/external/dompurify": ["./src/external/dompurify/index.ts"],