Feature: DataType tracked references

Wires up the server endpoint.

Fixes https://github.com/umbraco/Umbraco-CMS/issues/16390
This commit is contained in:
leekelleher
2024-05-26 17:35:03 +01:00
parent f59010fbbe
commit c0434aae7e
8 changed files with 254 additions and 21 deletions

View File

@@ -0,0 +1 @@
export * from './repository/index.js';

View File

@@ -0,0 +1,4 @@
import { manifests as repositoryManifests } from './repository/manifests.js';
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
export const manifests: Array<ManifestTypes> = [...repositoryManifests];

View File

@@ -0,0 +1,38 @@
import { UmbDataTypeReferenceServerDataSource } from './data-type-reference.server.data.js';
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import type { DataTypeReferenceResponseModel } from '@umbraco-cms/backoffice/external/backend-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
export type UmbDataTypeReferenceModel = {
unique: string;
entityType: string;
properties: Array<{ name: string; alias: string }>;
};
export class UmbDataTypeReferenceRepository extends UmbControllerBase {
#referenceSource: UmbDataTypeReferenceServerDataSource;
constructor(host: UmbControllerHost) {
super(host);
this.#referenceSource = new UmbDataTypeReferenceServerDataSource(this);
}
async requestReferencedBy(unique: string) {
if (!unique) throw new Error(`unique is required`);
const { data } = await this.#referenceSource.getReferencedBy(unique);
if (!data) return;
return data.map(mapper);
}
}
const mapper = (item: DataTypeReferenceResponseModel): UmbDataTypeReferenceModel => {
return {
unique: item.id,
entityType: item.type,
properties: item.properties,
};
};
export default UmbDataTypeReferenceRepository;

View File

@@ -0,0 +1,31 @@
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
import { DataTypeService } from '@umbraco-cms/backoffice/external/backend-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
/**
* @export
* @class UmbDataTypeReferenceServerDataSource
* @implements {RepositoryDetailDataSource}
*/
export class UmbDataTypeReferenceServerDataSource {
#host: UmbControllerHost;
/**
* Creates an instance of UmbDataTypeReferenceServerDataSource.
* @param {UmbControllerHost} host
* @memberof UmbDataTypeReferenceServerDataSource
*/
constructor(host: UmbControllerHost) {
this.#host = host;
}
/**
* Fetches the item for the given unique from the server
* @param {string} id
* @return {*}
* @memberof UmbDataTypeReferenceServerDataSource
*/
async getReferencedBy(id: string) {
return await tryExecuteAndNotify(this.#host, DataTypeService.getDataTypeByIdReferences({ id }));
}
}

View File

@@ -0,0 +1 @@
export * from './data-type-reference.repository.js';

View File

@@ -0,0 +1,12 @@
import type { ManifestRepository, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
export const UMB_DATA_TYPE_REFERENCE_REPOSITORY_ALIAS = 'Umb.Repository.DataType.Reference';
const repository: ManifestRepository = {
type: 'repository',
alias: UMB_DATA_TYPE_REFERENCE_REPOSITORY_ALIAS,
name: 'Data Type Reference Repository',
api: () => import('./data-type-reference.repository.js'),
};
export const manifests: Array<ManifestTypes> = [repository];

View File

@@ -0,0 +1,117 @@
import { UmbDataTypeReferenceRepository } from '../../../reference/index.js';
import type { UmbDataTypeReferenceModel } from '../../../reference/index.js';
import { css, html, customElement, state, repeat, property, when } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/modal';
import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router';
import type { UmbModalRouteBuilder } from '@umbraco-cms/backoffice/router';
const elementName = 'umb-data-type-workspace-view-info-reference';
@customElement(elementName)
export class UmbDataTypeWorkspaceViewInfoReferenceElement extends UmbLitElement {
#referenceRepository = new UmbDataTypeReferenceRepository(this);
#routeBuilder?: UmbModalRouteBuilder;
@property()
dataTypeUnique = '';
@state()
private _loading = true;
@state()
private _items?: Array<UmbDataTypeReferenceModel> = [];
constructor() {
super();
new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL)
.addAdditionalPath(':entityType')
.onSetup((params) => {
return { data: { entityType: params.entityType, preset: {} } };
})
.observeRouteBuilder((routeBuilder) => {
this.#routeBuilder = routeBuilder;
});
}
protected firstUpdated() {
this.#getReferences();
}
async #getReferences() {
this._loading = true;
const items = await this.#referenceRepository.requestReferencedBy(this.dataTypeUnique);
if (!items) return;
this._items = items;
this._loading = false;
}
render() {
return html`
<uui-box headline=${this.localize.term('references_tabName')}>
${when(
this._loading,
() => html`<uui-loader></uui-loader>`,
() => this.#renderItems(),
)}
</uui-box>
`;
}
#getEditPath(item: UmbDataTypeReferenceModel) {
// TODO: [LK] Ask NL for a reminder on how the route constants work.
return this.#routeBuilder ? this.#routeBuilder({ entityType: item.entityType }) + `edit/${item.unique}` : '#';
}
#renderItems() {
if (!this._items?.length) return html`<p>${this.localize.term('references_DataTypeNoReferences')}</p>`;
return html`
<uui-table>
<uui-table-head>
<uui-table-head-cell><umb-localize key="general_name">Name</umb-localize></uui-table-head-cell>
<uui-table-head-cell><umb-localize key="general_type">Type</umb-localize></uui-table-head-cell>
<uui-table-head-cell>
<umb-localize key="references_usedByProperties">Referenced by</umb-localize>
</uui-table-head-cell>
</uui-table-head>
${repeat(
this._items,
(item) => item.unique,
(item) => html`
<uui-table-row>
<uui-table-cell>
<uui-ref-node-document-type href=${this.#getEditPath(item)} name=${item.unique}>
<umb-icon slot="icon" name="icon-document"></umb-icon>
</uui-ref-node-document-type>
</uui-table-cell>
<uui-table-cell>${item.entityType}</uui-table-cell>
<uui-table-cell>${item.properties.map((prop) => prop.name).join(', ')}</uui-table-cell>
</uui-table-row>
`,
)}
</uui-table>
`;
}
static styles = [
UmbTextStyles,
css`
uui-table-cell {
color: var(--uui-color-text-alt);
}
`,
];
}
export { UmbDataTypeWorkspaceViewInfoReferenceElement as element };
declare global {
interface HTMLElementTagNameMap {
[elementName]: UmbDataTypeWorkspaceViewInfoReferenceElement;
}
}

View File

@@ -4,10 +4,12 @@ import { css, html, customElement, state } from '@umbraco-cms/backoffice/externa
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/extension-registry';
import './data-type-workspace-view-info-reference.element.js';
@customElement('umb-workspace-view-data-type-info')
export class UmbWorkspaceViewDataTypeInfoElement extends UmbLitElement implements UmbWorkspaceViewElement {
@state()
_unique?: string;
_unique: string = '';
@state()
_schemaAlias?: string;
@@ -30,7 +32,7 @@ export class UmbWorkspaceViewDataTypeInfoElement extends UmbLitElement implement
if (!this._workspaceContext) return;
this.observe(this._workspaceContext.unique, (unique) => {
this._unique = unique;
this._unique = unique!;
});
this.observe(this._workspaceContext.propertyEditorSchemaAlias, (schemaAlias) => {
@@ -43,36 +45,63 @@ export class UmbWorkspaceViewDataTypeInfoElement extends UmbLitElement implement
}
render() {
return html` ${this._renderGeneralInfo()}${this._renderReferences()} `;
}
private _renderGeneralInfo() {
return html`
<uui-box headline="General" style="margin-bottom: 20px;">
<umb-property-layout label="Id">
<div slot="editor">${this._unique}</div>
</umb-property-layout>
<umb-property-layout label="Property Editor Alias">
<div slot="editor">${this._schemaAlias}</div>
</umb-property-layout>
<umb-property-layout label="Property Editor UI Alias">
<div slot="editor">${this._uiAlias}</div>
</umb-property-layout>
</uui-box>
<div class="container">
<umb-data-type-workspace-view-info-reference
.dataTypeUnique=${this._unique}></umb-data-type-workspace-view-info-reference>
</div>
<div class="container">${this.#renderGeneralInfo()}</div>
`;
}
private _renderReferences() {
return html` <uui-box headline="References"> </uui-box> `;
#renderGeneralInfo() {
return html`
<uui-box id="general-section" headline="General">
<div class="general-item">
<strong><umb-localize key="template_id">Id</umb-localize></strong>
<span>${this._unique}</span>
</div>
<div class="general-item">
<strong>Property Editor Schema Alias</strong>
<span>${this._schemaAlias}</span>
</div>
<div class="general-item">
<strong>Property Editor UI Alias</strong>
<span>${this._uiAlias}</span>
</div>
</uui-box>
`;
}
static styles = [
UmbTextStyles,
css`
:host {
display: block;
display: grid;
gap: var(--uui-size-layout-1);
padding: var(--uui-size-layout-1);
grid-template-columns: 1fr 350px;
}
.container {
display: flex;
flex-direction: column;
gap: var(--uui-size-layout-1);
}
#general-section {
display: flex;
flex-direction: column;
}
.general-item {
display: flex;
flex-direction: column;
gap: var(--uui-size-space-1);
}
.general-item:not(:last-child) {
margin-bottom: var(--uui-size-space-6);
}
`,
];