Merge pull request #1780 from umbraco/bugfix/examine-dashboard-status-and-modals

Bugfix: examine dashboard status and modals
This commit is contained in:
Lee Kelleher
2024-05-08 15:34:01 +01:00
committed by GitHub
21 changed files with 346 additions and 226 deletions

View File

@@ -588,7 +588,7 @@ export default {
examineManagement: {
configuredSearchers: 'Konfigurerede søgere',
configuredSearchersDescription:
'Viser egenskaber og værktøjer til enhver konfigureret søger (dvs. som en\n multi-indekssøger)\n ',
'Viser egenskaber og værktøjer til enhver konfigureret søger (dvs. som en multi-indekssøger)',
fieldValues: 'Feltværdier',
healthStatus: 'Sundhedstilstand',
healthStatusDescription: 'Indeksets sundhedstilstand, og hvis det kan læses',
@@ -597,10 +597,10 @@ export default {
indexInfoDescription: 'Viser indeksets egenskaber',
manageIndexes: 'Administrer Examine indekserne',
manageIndexesDescription:
'Giver dig mulighed for at se detaljerne for hvert indeks og giver nogle\n værktøjer til styring af indeksørerne\n ',
'Giver dig mulighed for at se detaljerne for hvert indeks og giver nogle værktøjer til styring af indeksørerne',
rebuildIndex: 'Genopbyg indeks',
rebuildIndexWarning:
'\n Dette vil medføre, at indekset genopbygges.<br />\n Afhængigt af hvor meget indhold der er på dit website, kan det tage et stykke tid.<br />\n Det anbefales ikke at genopbygge et indeks i perioder med høj websitetrafik eller når redaktører redigerer indhold.\n ',
'Dette vil medføre, at indekset genopbygges.<br /> Afhængigt af hvor meget indhold der er på dit website, kan det tage et stykke tid.<br /> Det anbefales ikke at genopbygge et indeks i perioder med høj websitetrafik eller når redaktører redigerer indhold.',
searchers: 'Søgere',
searchDescription: 'Søg i indekset og se resultaterne',
tools: 'Værktøjer',
@@ -608,7 +608,7 @@ export default {
fields: 'felter',
indexCannotRead: 'Indexet skal bygges igen, for at kunne læses',
processIsTakingLonger:
'Processen tager længere tid end forventet. Kontrollér Umbraco loggen for at se om\n der er sket fejl under operationen\n ',
'Processen tager længere tid end forventet. Kontrollér Umbraco loggen for at se om der er sket fejl under operationen',
indexCannotRebuild: 'Dette index kan ikke genbygges for det ikke har nogen',
iIndexPopulator: 'IIndexPopulator',
contentInIndex: 'Content in index',

View File

@@ -597,7 +597,7 @@ export default {
examineManagement: {
configuredSearchers: 'Configured Searchers',
configuredSearchersDescription:
'Shows properties and tools for any configured Searcher (i.e. such as a\n multi-index searcher)\n ',
'Shows properties and tools for any configured Searcher (i.e. such as a multi-index searcher)',
fieldValues: 'Field values',
healthStatus: 'Health status',
healthStatusDescription: 'The health status of the index and if it can be read',
@@ -607,10 +607,10 @@ export default {
indexInfoDescription: 'Lists the properties of the index',
manageIndexes: "Manage Examine's indexes",
manageIndexesDescription:
'Allows you to view the details of each index and provides some tools for\n managing the indexes\n ',
'Allows you to view the details of each index and provides some tools for managing the indexes',
rebuildIndex: 'Rebuild index',
rebuildIndexWarning:
'\n This will cause the index to be rebuilt.<br />\n Depending on how much content there is in your site this could take a while.<br />\n It is not recommended to rebuild an index during times of high website traffic or when editors are editing content.\n ',
'This will cause the index to be rebuilt.<br /> Depending on how much content there is in your site this could take a while.<br /> It is not recommended to rebuild an index during times of high website traffic or when editors are editing content.',
searchers: 'Searchers',
searchDescription: 'Search the index and view the results',
tools: 'Tools',
@@ -618,7 +618,7 @@ export default {
fields: 'fields',
indexCannotRead: 'The index cannot be read and will need to be rebuilt',
processIsTakingLonger:
'The process is taking longer than expected, check the Umbraco log to see if there\n have been any errors during this operation\n ',
'The process is taking longer than expected, check the Umbraco log to see if there have been any errors during this operation',
indexCannotRebuild: 'This index cannot be rebuilt because it has no assigned',
iIndexPopulator: 'IIndexPopulator',
},

View File

@@ -979,6 +979,11 @@ export enum HealthStatusModel {
REBUILDING = 'Rebuilding'
}
export type HealthStatusResponseModel = {
status: HealthStatusModel
message?: string | null
};
export type HelpPageResponseModel = {
name?: string | null
description?: string | null
@@ -993,7 +998,7 @@ parent?: ReferenceByIdModel | null
export type IndexResponseModel = {
name: string
healthStatus: HealthStatusModel
healthStatus: HealthStatusResponseModel
canRebuild: boolean
searcherName: string
documentCount: number
@@ -5236,15 +5241,15 @@ PostWebhook: {
GetWebhookById: {
id: string
};
DeleteWebhookById: {
id: string
};
PutWebhookById: {
id: string
requestBody?: UpdateWebhookRequestModel
};
DeleteWebhookById: {
id: string
};
GetWebhookEvents: {
skip?: number
@@ -5259,8 +5264,8 @@ take?: number
,GetWebhook: PagedWebhookResponseModel
,PostWebhook: string
,GetWebhookById: WebhookResponseModel
,PutWebhookById: string
,DeleteWebhookById: string
,PutWebhookById: string
,GetWebhookEvents: PagedWebhookEventModel
}

View File

@@ -9022,20 +9022,17 @@ take
* @returns string Success
* @throws ApiError
*/
public static putWebhookById(data: WebhookData['payloads']['PutWebhookById']): CancelablePromise<WebhookData['responses']['PutWebhookById']> {
public static deleteWebhookById(data: WebhookData['payloads']['DeleteWebhookById']): CancelablePromise<WebhookData['responses']['DeleteWebhookById']> {
const {
id,
requestBody
id
} = data;
return __request(OpenAPI, {
method: 'PUT',
method: 'DELETE',
url: '/umbraco/management/api/v1/webhook/{id}',
path: {
id
},
body: requestBody,
mediaType: 'application/json',
responseHeader: 'Umb-Notifications',
errors: {
400: `Bad Request`,
@@ -9050,17 +9047,20 @@ requestBody
* @returns string Success
* @throws ApiError
*/
public static deleteWebhookById(data: WebhookData['payloads']['DeleteWebhookById']): CancelablePromise<WebhookData['responses']['DeleteWebhookById']> {
public static putWebhookById(data: WebhookData['payloads']['PutWebhookById']): CancelablePromise<WebhookData['responses']['PutWebhookById']> {
const {
id
id,
requestBody
} = data;
return __request(OpenAPI, {
method: 'DELETE',
method: 'PUT',
url: '/umbraco/management/api/v1/webhook/{id}',
path: {
id
},
body: requestBody,
mediaType: 'application/json',
responseHeader: 'Umb-Notifications',
errors: {
400: `Bad Request`,

View File

@@ -20,7 +20,7 @@ export const Indexers: IndexResponseModel[] = [
{
name: 'ExternalIndex',
canRebuild: true,
healthStatus: HealthStatusModel.HEALTHY,
healthStatus: { status: HealthStatusModel.HEALTHY },
documentCount: 0,
fieldCount: 0,
searcherName: '',
@@ -40,7 +40,7 @@ export const Indexers: IndexResponseModel[] = [
{
name: 'InternalIndex',
canRebuild: true,
healthStatus: HealthStatusModel.HEALTHY,
healthStatus: { status: HealthStatusModel.HEALTHY },
documentCount: 0,
fieldCount: 0,
searcherName: '',
@@ -60,7 +60,7 @@ export const Indexers: IndexResponseModel[] = [
{
name: 'MemberIndex',
canRebuild: true,
healthStatus: HealthStatusModel.HEALTHY,
healthStatus: { status: HealthStatusModel.HEALTHY },
fieldCount: 0,
documentCount: 0,
searcherName: '',

View File

@@ -4,7 +4,6 @@ export * from './confirm-modal.token.js';
export * from './debug-modal.token.js';
export * from './embedded-media-modal.token.js';
export * from './entity-user-permission-settings-modal.token.js';
export * from './examine-fields-settings-modal.token.js';
export * from './icon-picker-modal.token.js';
export * from './item-picker-modal.token.js';
export * from './link-picker-modal.token.js';

View File

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

View File

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

View File

@@ -0,0 +1,76 @@
import type {
UmbExamineFieldsSettingsModalData,
UmbExamineFieldsSettingsModalValue,
UmbExamineFieldSettingsType,
} from './examine-fields-settings-modal.token.js';
import { html, css, customElement } from '@umbraco-cms/backoffice/external/lit';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
@customElement('umb-examine-fields-settings-modal')
export class UmbExamineFieldsSettingsModalElement extends UmbModalBaseElement<
UmbExamineFieldsSettingsModalData,
UmbExamineFieldsSettingsModalValue
> {
render() {
return html`<umb-body-layout headline=${this.localize.term('examineManagement_fields')}>
<uui-scroll-container id="field-settings"> ${this.#renderFields()} </uui-scroll-container>
<div slot="actions">
<uui-button
look="primary"
label=${this.localize.term('general_close')}
@click="${this._submitModal}"></uui-button>
</div>
</umb-body-layout>`;
}
#setExposed(fieldSetting: UmbExamineFieldSettingsType) {
const newField: UmbExamineFieldSettingsType = { ...fieldSetting, exposed: !fieldSetting.exposed };
const updatedFields =
this.modalContext?.getValue().fields.map((field) => {
if (field.name === fieldSetting.name) return newField;
else return field;
}) ?? [];
this.modalContext?.updateValue({ fields: updatedFields });
}
#renderFields() {
if (!this.value.fields.length) return;
return html`<span>
${Object.values(this.value.fields).map((field) => {
return html`<uui-toggle
name="${field.name}"
label="${field.name}"
.checked="${field.exposed}"
@change="${() => this.#setExposed(field)}"></uui-toggle>
<br />`;
})}
</span>`;
}
static styles = [
UmbTextStyles,
css`
:host {
display: relative;
}
uui-scroll-container {
overflow-y: scroll;
max-height: 100%;
min-height: 0;
flex: 1;
}
`,
];
}
export default UmbExamineFieldsSettingsModalElement;
declare global {
interface HTMLElementTagNameMap {
'umb-examine-fields-settings-modal': UmbExamineFieldsSettingsModalElement;
}
}

View File

@@ -1,20 +1,20 @@
import { UmbModalToken } from './modal-token.js';
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
export type UmbExamineFieldsSettingsModalData = never;
type FieldSettingsType = {
export type UmbExamineFieldSettingsType = {
name: string;
exposed: boolean;
};
export type UmbExamineFieldsSettingsModalValue = {
fields: Array<FieldSettingsType>;
fields: Array<UmbExamineFieldSettingsType>;
};
export const UMB_EXAMINE_FIELDS_SETTINGS_MODAL = new UmbModalToken<
UmbExamineFieldsSettingsModalData,
UmbExamineFieldsSettingsModalValue
>('Umb.Modal.ExamineFieldsSettings', {
>('Umb.Modal.Examine.FieldsSettings', {
modal: {
type: 'sidebar',
size: 'small',

View File

@@ -0,0 +1,2 @@
export * from './examine-fields-settings-modal.element.js';
export * from './examine-fields-settings-modal.token.js';

View File

@@ -1,11 +1,15 @@
import type {
UmbExamineFieldsViewerModalData,
UmbExamineFieldsViewerModalValue,
} from './examine-fields-viewer-modal.token.js';
import { html, css, nothing, customElement } from '@umbraco-cms/backoffice/external/lit';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
import type { SearchResultResponseModel } from '@umbraco-cms/backoffice/external/backend-api';
@customElement('umb-modal-element-fields-viewer')
export class UmbModalElementFieldsViewerElement extends UmbModalBaseElement<
SearchResultResponseModel & { name: string }
@customElement('umb-examine-fields-viewer-modal')
export class UmbExamineFieldsViewerModalElement extends UmbModalBaseElement<
UmbExamineFieldsViewerModalData,
UmbExamineFieldsViewerModalValue
> {
private _handleClose() {
this.modalContext?.reject();
@@ -15,7 +19,7 @@ export class UmbModalElementFieldsViewerElement extends UmbModalBaseElement<
if (!this.data) return nothing;
return html`
<uui-dialog-layout class="uui-text" headline="${this.data.name}">
<umb-body-layout headline="${this.data?.name}">
<uui-scroll-container id="field-viewer">
<span>
<uui-table>
@@ -23,7 +27,7 @@ export class UmbModalElementFieldsViewerElement extends UmbModalBaseElement<
<uui-table-head-cell> Field </uui-table-head-cell>
<uui-table-head-cell> Value </uui-table-head-cell>
</uui-table-head>
${Object.values(this.data.fields ?? []).map((cell) => {
${Object.values(this.data.searchResult.fields ?? []).map((cell) => {
return html`<uui-table-row>
<uui-table-cell> ${cell.name} </uui-table-cell>
<uui-table-cell> ${cell.values?.join(', ')} </uui-table-cell>
@@ -32,10 +36,13 @@ export class UmbModalElementFieldsViewerElement extends UmbModalBaseElement<
</uui-table>
</span>
</uui-scroll-container>
<div>
<uui-button look="primary" @click="${this._handleClose}">Close</uui-button>
<div slot="actions">
<uui-button
look="primary"
label=${this.localize.term('general_close')}
@click=${this._rejectModal}></uui-button>
</div>
</uui-dialog-layout>
</umb-body-layout>
`;
}
@@ -45,11 +52,6 @@ export class UmbModalElementFieldsViewerElement extends UmbModalBaseElement<
:host {
display: relative;
}
uui-dialog-layout {
display: flex;
flex-direction: column;
height: 100%;
}
span {
display: block;
@@ -57,22 +59,18 @@ export class UmbModalElementFieldsViewerElement extends UmbModalBaseElement<
}
uui-scroll-container {
line-height: 0;
overflow-y: scroll;
max-height: 100%;
min-height: 0;
}
div {
margin-top: var(--uui-size-space-5);
display: flex;
flex-direction: row-reverse;
}
`,
];
}
export default UmbExamineFieldsViewerModalElement;
declare global {
interface HTMLElementTagNameMap {
'umb-modal-element-fields-viewer': UmbModalElementFieldsViewerElement;
'umb-examine-fields-viewer-modal': UmbExamineFieldsViewerModalElement;
}
}

View File

@@ -0,0 +1,19 @@
import type { SearchResultResponseModel } from '@umbraco-cms/backoffice/external/backend-api';
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
export type UmbExamineFieldsViewerModalData = {
name: string;
searchResult: SearchResultResponseModel;
};
export type UmbExamineFieldsViewerModalValue = never;
export const UMB_EXAMINE_FIELDS_VIEWER_MODAL = new UmbModalToken<
UmbExamineFieldsViewerModalData,
UmbExamineFieldsViewerModalValue
>('Umb.Modal.Examine.FieldsViewer', {
modal: {
type: 'sidebar',
size: 'small',
},
});

View File

@@ -0,0 +1,2 @@
export * from './examine-fields-viewer-modal.element.js';
export * from './examine-fields-viewer-modal.token.js';

View File

@@ -0,0 +1,2 @@
export * from './fields-settings/index.js';
export * from './fields-viewer/index.js';

View File

@@ -0,0 +1,18 @@
import type { ManifestModal, ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
const modals: Array<ManifestModal> = [
{
type: 'modal',
alias: 'Umb.Modal.Examine.FieldsSettings',
name: 'Examine Field Settings Modal',
js: () => import('./fields-settings/examine-fields-settings-modal.element.js'),
},
{
type: 'modal',
alias: 'Umb.Modal.Examine.FieldsViewer',
name: 'Examine Field Viewer Modal',
js: () => import('./fields-viewer/examine-fields-viewer-modal.element.js'),
},
];
export const manifests: Array<ManifestTypes> = [...modals];

View File

@@ -1,81 +0,0 @@
import { html, css, customElement } from '@umbraco-cms/backoffice/external/lit';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import type {
UmbExamineFieldsSettingsModalValue,
UmbExamineFieldsSettingsModalData} from '@umbraco-cms/backoffice/modal';
import {
UmbModalBaseElement,
} from '@umbraco-cms/backoffice/modal';
@customElement('umb-examine-fields-settings-modal')
export default class UmbExamineFieldsSettingsModalElement extends UmbModalBaseElement<
UmbExamineFieldsSettingsModalData,
UmbExamineFieldsSettingsModalValue
> {
render() {
if (this.value.fields) {
return html`
<uui-dialog-layout headline="Show fields">
<uui-scroll-container id="field-settings">
<span>
${Object.values(this.value.fields).map((field, index) => {
return html`<uui-toggle
name="${field.name}"
label="${field.name}"
.checked="${field.exposed}"
@change="${() => {
this.value.fields ? (this.value.fields[index].exposed = !field.exposed) : '';
}}"></uui-toggle>
<br />`;
})}
</span>
</uui-scroll-container>
<div>
<uui-button look="primary" label="Close sidebar" @click="${this._submitModal}">Close</uui-button>
</div>
</uui-dialog-layout>
`;
} else {
return '';
}
}
static styles = [
UmbTextStyles,
css`
:host {
display: relative;
}
uui-dialog-layout {
display: flex;
flex-direction: column;
height: 100%;
background-color: var(--uui-color-surface);
box-shadow: var(--uui-shadow-depth-1, 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24));
border-radius: var(--uui-border-radius);
padding: var(--uui-size-space-5);
box-sizing: border-box;
}
uui-scroll-container {
overflow-y: scroll;
max-height: 100%;
min-height: 0;
flex: 1;
}
div {
margin-top: var(--uui-size-space-5);
display: flex;
flex-direction: row-reverse;
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
'umb-examine-fields-settings-modal': UmbExamineFieldsSettingsModalElement;
}
}

View File

@@ -1,7 +1,7 @@
import type { UUIButtonState } from '@umbraco-cms/backoffice/external/uui';
import { css, html, nothing, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
import { umbConfirmModal } from '@umbraco-cms/backoffice/modal';
import type { IndexResponseModel } from '@umbraco-cms/backoffice/external/backend-api';
import type { HealthStatusResponseModel, IndexResponseModel } from '@umbraco-cms/backoffice/external/backend-api';
import { HealthStatusModel, IndexerService } from '@umbraco-cms/backoffice/external/backend-api';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
@@ -25,35 +25,51 @@ export class UmbDashboardExamineIndexElement extends UmbLitElement {
connectedCallback() {
super.connectedCallback();
this._getIndexData();
this.#loadData();
}
private async _getIndexData() {
async #loadData() {
this._indexData = await this.#getIndexData();
if (this._indexData?.healthStatus.status === HealthStatusModel.REBUILDING) {
this._buttonState = 'waiting';
this._continuousPolling();
} else {
this._loading = false;
}
}
async #getIndexData() {
const { data } = await tryExecuteAndNotify(
this,
IndexerService.getIndexerByIndexName({ indexName: this.indexName }),
);
this._indexData = data;
return data;
}
// TODO: Add continuous polling to update the status
if (this._indexData?.healthStatus === HealthStatusModel.REBUILDING) {
this._buttonState = 'waiting';
private async _continuousPolling() {
//Checking the server every 5 seconds to see if the index is still rebuilding.
while (this._buttonState === 'waiting') {
await new Promise((resolve) => setTimeout(resolve, 5000));
this._indexData = await this.#getIndexData();
if (this._indexData?.healthStatus.status !== HealthStatusModel.REBUILDING) {
this._buttonState = 'success';
}
}
this._loading = false;
return;
}
private async _onRebuildHandler() {
await umbConfirmModal(this, {
headline: `Rebuild ${this.indexName}`,
content: html`
This will cause the index to be rebuilt.<br />
headline: `${this.localize.term('examineManagement_rebuildIndex')} ${this.indexName}`,
content: html`<umb-localize key="examineManagement_rebuildIndexWarning"
>This will cause the index to be rebuilt.<br />
Depending on how much content there is in your site this could take a while.<br />
It is not recommended to rebuild an index during times of high website traffic or when editors are editing
content.
`,
content.</umb-localize
> `,
color: 'danger',
confirmLabel: 'Rebuild',
confirmLabel: this.localize.term('examineManagement_rebuildIndex'),
});
this._rebuild();
@@ -68,9 +84,22 @@ export class UmbDashboardExamineIndexElement extends UmbLitElement {
this._buttonState = 'failed';
return;
}
this._buttonState = 'success';
await this._getIndexData();
await this.#loadData();
}
#renderHealthStatus(healthStatus: HealthStatusResponseModel) {
const msg = healthStatus.message ? healthStatus.message : healthStatus.status;
switch (healthStatus.status) {
case HealthStatusModel.HEALTHY:
return html`<umb-icon name="icon-check color-green"></umb-icon>${msg}`;
case HealthStatusModel.UNHEALTHY:
return html`<umb-icon name="icon-error color-red"></umb-icon>${msg}`;
case HealthStatusModel.REBUILDING:
return html`<umb-icon name="icon-time color-yellow"></umb-icon>${msg}`;
default:
return;
}
}
render() {
@@ -79,35 +108,33 @@ export class UmbDashboardExamineIndexElement extends UmbLitElement {
return html`
<uui-box headline="${this.indexName}">
<p>
<strong>Health Status</strong><br />
The health status of the ${this.indexName} and if it can be read
<strong><umb-localize key="examineManagement_healthStatus">Health Status</umb-localize></strong
><br />
<umb-localize key="examineManagement_healthStatusDescription"
>The health status of the ${this.indexName} and if it can be read</umb-localize
>
</p>
<div>
<uui-icon-essentials>
${
this._indexData.healthStatus === HealthStatusModel.UNHEALTHY
? html`<uui-icon name="wrong" class="danger"></uui-icon>`
: html`<uui-icon name="check" class="positive"></uui-icon>`
}
</uui-icon>
</uui-icon-essentials>
${this._indexData.healthStatus}
</div>
<div id="health-status">${this.#renderHealthStatus(this._indexData.healthStatus)}</div>
</uui-box>
${this.renderIndexSearch()} ${this.renderPropertyList()} ${this.renderTools()}
`;
}
private renderIndexSearch() {
if (!this._indexData || this._indexData.healthStatus !== HealthStatusModel.HEALTHY) return nothing;
// Do we want to show the search while rebuilding?
if (!this._indexData || this._indexData.healthStatus.status === HealthStatusModel.REBUILDING) return nothing;
return html`<umb-dashboard-examine-searcher .searcherName="${this.indexName}"></umb-dashboard-examine-searcher>`;
}
private renderPropertyList() {
if (!this._indexData) return nothing;
return html`<uui-box headline="Index info">
<p>Lists the properties of the ${this.indexName}</p>
return html`<uui-box headline=${this.localize.term('examineManagement_indexInfo')}>
<p>
<umb-localize key="examineManagement_indexInfoDescription"
>Lists the properties of the ${this.indexName}</umb-localize
>
</p>
<uui-table class="info">
<uui-table-row>
<uui-table-cell style="width:0px; font-weight: bold;"> documentCount </uui-table-cell>
@@ -130,23 +157,26 @@ export class UmbDashboardExamineIndexElement extends UmbLitElement {
}
private renderTools() {
return html` <uui-box headline="Tools">
<p>Tools to manage the ${this.indexName}</p>
return html` <uui-box headline=${this.localize.term('examineManagement_tools')}>
<p><umb-localize key="examineManagement_toolsDescription">Tools to manage the ${this.indexName}</umb-localize></p>
<uui-button
color="danger"
look="primary"
.state="${this._buttonState}"
@click="${this._onRebuildHandler}"
.disabled="${this._indexData?.canRebuild ? false : true}"
label="Rebuild index">
Rebuild
</uui-button>
label=${this.localize.term('examineManagement_rebuildIndex')}></uui-button>
</uui-box>`;
}
static styles = [
UmbTextStyles,
css`
#health-status {
display: flex;
gap: var(--uui-size-6);
}
:host {
display: block;
}
@@ -190,13 +220,6 @@ export class UmbDashboardExamineIndexElement extends UmbLitElement {
padding-right: var(--uui-size-space-5);
}
.positive {
color: var(--uui-color-positive);
}
.danger {
color: var(--uui-color-danger);
}
button {
background: none;
border: none;

View File

@@ -40,19 +40,38 @@ export class UmbDashboardExamineOverviewElement extends UmbLitElement {
this._loadingSearchers = false;
}
#renderStatus(status: HealthStatusModel) {
switch (status) {
case HealthStatusModel.HEALTHY:
return html`<umb-icon name="icon-check color-green"></umb-icon>`;
case HealthStatusModel.UNHEALTHY:
return html`<umb-icon name="icon-error color-red"></umb-icon>`;
case HealthStatusModel.REBUILDING:
return html`<umb-icon name="icon-time color-yellow"></umb-icon>`;
default:
return;
}
}
render() {
return html`
<uui-box headline="Indexers" class="overview">
<uui-box headline=${this.localize.term('examineManagement_indexers')} class="overview">
<p>
<strong>Manage Examine's indexes</strong><br />
Allows you to view the details of each index and provides some tools for managing the indexes
<strong><umb-localize key="examineManagement_manageIndexes">Manage Examine's indexes</umb-localize></strong
><br />
<umb-localize key="examineManagement_manageIndexesDescription"
>Allows you to view the details of each index and provides some tools for managing the indexes</umb-localize
>
</p>
${this.renderIndexersList()}
</uui-box>
<uui-box headline="Searchers">
<uui-box headline=${this.localize.term('examineManagement_searchers')}>
<p>
<strong>Configured Searchers</strong><br />
Shows properties and tools for any configured Searcher (i.e. such as a multi-index searcher)
<strong><umb-localize key="examineManagement_configuredSearchers">Configured Searchers</umb-localize></strong
><br />
<umb-localize key="examineManagement_configuredSearchersDescription"
>Shows properties and tools for any configured Searcher (i.e. such as a multi-index searcher)</umb-localize
>
</p>
${this.renderSearchersList()}
</uui-box>
@@ -66,16 +85,7 @@ export class UmbDashboardExamineOverviewElement extends UmbLitElement {
${this._indexers.map((index) => {
return html`
<uui-table-row>
<uui-table-cell style="width:0px">
<uui-icon-essentials>
${
index.healthStatus === HealthStatusModel.UNHEALTHY
? html`<uui-icon name="wrong" class="danger"></uui-icon>`
: html`<uui-icon name="check" class="positive"></uui-icon>`
}
</uui-icon>
</uui-icon-essentials>
</uui-table-cell>
<uui-table-cell style="width:0px"> ${this.#renderStatus(index.healthStatus.status)} </uui-table-cell>
<uui-table-cell>
<a href="${window.location.href.replace(/\/+$/, '')}/index/${index.name}">${index.name}</a>
</uui-table-cell>

View File

@@ -1,14 +1,16 @@
import { UMB_EXAMINE_FIELDS_SETTINGS_MODAL, UMB_EXAMINE_FIELDS_VIEWER_MODAL } from '../modal/index.js';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { css, html, nothing, customElement, state, query, property } from '@umbraco-cms/backoffice/external/lit';
import { UMB_MODAL_MANAGER_CONTEXT, UMB_EXAMINE_FIELDS_SETTINGS_MODAL } from '@umbraco-cms/backoffice/modal';
import {
UMB_MODAL_MANAGER_CONTEXT,
UMB_WORKSPACE_MODAL,
UmbModalRouteRegistrationController,
} from '@umbraco-cms/backoffice/modal';
import type { SearchResultResponseModel, FieldPresentationModel } from '@umbraco-cms/backoffice/external/backend-api';
import { SearcherService } from '@umbraco-cms/backoffice/external/backend-api';
import { UmbLitElement, umbFocus } from '@umbraco-cms/backoffice/lit-element';
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
import './modal-views/fields-viewer.element.js';
import './modal-views/fields-settings-modal.element.js';
interface ExposedSearchResultField {
name: string;
exposed: boolean;
@@ -31,15 +33,27 @@ export class UmbDashboardExamineSearcherElement extends UmbLitElement {
@query('#search-input')
private _searchInput!: HTMLInputElement;
private _onNameClick() {
// TODO:
alert('TODO: Open workspace for ' + this.searcherName);
}
@state()
private _workspacePath = 'aa';
private _onKeyPress(e: KeyboardEvent) {
e.key == 'Enter' ? this._onSearch() : undefined;
}
#entityType = '';
constructor() {
super();
new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL)
.addAdditionalPath(':entityType')
.onSetup((routingInfo) => {
return { data: { entityType: routingInfo.entityType, preset: {} } };
})
.observeRouteBuilder((routeBuilder) => {
this._workspacePath = routeBuilder({ entityType: this.#entityType });
});
}
private async _onSearch() {
if (!this._searchInput.value.length) return;
this._searchLoading = true;
@@ -86,36 +100,48 @@ export class UmbDashboardExamineSearcherElement extends UmbLitElement {
const modalContext = modalManager.open(this, UMB_EXAMINE_FIELDS_SETTINGS_MODAL, {
value: { fields: this._exposedFields ?? [] },
});
modalContext?.onSubmit().then((value) => {
this._exposedFields = value.fields;
});
await modalContext.onSubmit().catch(() => undefined);
const value = modalContext.getValue();
this._exposedFields = value?.fields;
}
async #onFieldViewClick(rowData: SearchResultResponseModel) {
const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);
modalManager.open(this, 'umb-modal-element-fields-viewer', {
const modalContext = modalManager.open(this, UMB_EXAMINE_FIELDS_VIEWER_MODAL, {
modal: {
type: 'sidebar',
size: 'medium',
},
data: { ...rowData, name: this.getSearchResultNodeName(rowData) },
data: { searchResult: rowData, name: this.getSearchResultNodeName(rowData) },
});
await modalContext.onSubmit().catch(() => undefined);
}
render() {
return html`
<uui-box headline="Search">
<p>Search the ${this.searcherName} and view the results</p>
<uui-box headline=${this.localize.term('general_search')}>
<p>
<umb-localize key="examineManagement_searchDescription"
>Search the ${this.searcherName} and view the results</umb-localize
>
</p>
<div class="flex">
<uui-input
type="search"
id="search-input"
placeholder="Type to filter..."
label="Type to filter"
placeholder=${this.localize.term('placeholders_filter')}
label=${this.localize.term('placeholders_filter')}
@keypress=${this._onKeyPress}
${umbFocus()}>
</uui-input>
<uui-button color="positive" look="primary" label="Search" @click="${this._onSearch}"> Search </uui-button>
<uui-button
color="positive"
look="primary"
label=${this.localize.term('general_search')}
@click="${this._onSearch}"></uui-button>
</div>
${this.renderSearchResults()}
</uui-box>
@@ -128,28 +154,44 @@ export class UmbDashboardExamineSearcherElement extends UmbLitElement {
return nodeNameField?.values?.join(', ') ?? '';
}
#getEntityTypeFromIndexType(indexType: string) {
switch (indexType) {
case 'content':
return 'document';
default:
return indexType;
}
}
private renderSearchResults() {
if (this._searchLoading) return html`<uui-loader></uui-loader>`;
if (!this._searchResults) return nothing;
if (!this._searchResults.length) {
return html`<p>No results found</p>`;
return html`<p>${this.localize.term('examineManagement_noResults')}</p>`;
}
return html`<div class="table-container">
<uui-scroll-container>
<uui-table class="search">
<uui-table-head>
<uui-table-head-cell style="width:0">Score</uui-table-head-cell>
<uui-table-head-cell style="width:0">Id</uui-table-head-cell>
<uui-table-head-cell>Name</uui-table-head-cell>
<uui-table-head-cell>Fields</uui-table-head-cell>
<uui-table-head-cell style="width:0">${this.localize.term('general_id')}</uui-table-head-cell>
<uui-table-head-cell>${this.localize.term('general_name')}</uui-table-head-cell>
<uui-table-head-cell>${this.localize.term('examineManagement_fields')}</uui-table-head-cell>
${this.renderHeadCells()}
</uui-table-head>
${this._searchResults?.map((rowData) => {
const indexType = rowData.fields?.find((field) => field.name === '__IndexType')?.values?.join(', ') ?? '';
this.#entityType = this.#getEntityTypeFromIndexType(indexType);
const unique = rowData.fields?.find((field) => field.name === '__Key')?.values?.join(', ') ?? '';
return html`<uui-table-row>
<uui-table-cell> ${rowData.score} </uui-table-cell>
<uui-table-cell> ${rowData.id} </uui-table-cell>
<uui-table-cell>
<uui-button look="secondary" label="Open workspace for this document" @click="${this._onNameClick}">
<uui-button
look="secondary"
label=${this.localize.term('actions_editContent')}
href=${this._workspacePath + this.#entityType + '/edit/' + unique}>
${this.getSearchResultNodeName(rowData)}
</uui-button>
</uui-table-cell>
@@ -157,9 +199,10 @@ export class UmbDashboardExamineSearcherElement extends UmbLitElement {
<uui-button
class="bright"
look="secondary"
label="Open sidebar to see all fields"
label=${this.localize.term('examineManagement_fieldValues')}
@click=${() => this.#onFieldViewClick(rowData)}>
${rowData.fields ? Object.keys(rowData.fields).length : ''} fields
${rowData.fields ? Object.keys(rowData.fields).length : ''}
${this.localize.term('examineManagement_fields')}
</uui-button>
</uui-table-cell>
${rowData.fields ? this.renderBodyCells(rowData.fields) : ''}
@@ -185,7 +228,7 @@ export class UmbDashboardExamineSearcherElement extends UmbLitElement {
<span>${field.name}</span>
<uui-button
look="secondary"
label="Close field ${field.name}"
label="${this.localize.term('actions_remove')} ${field.name}"
compact
@click="${() => {
this._exposedFields = this._exposedFields?.map((f) => {

View File

@@ -1,5 +1,8 @@
import { manifests as examineManifests } from './examine-management-dashboard/manifests.js';
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
import './examine-management-dashboard/index.js';
export const manifests: Array<ManifestTypes> = [
{
type: 'headerApp',
@@ -37,10 +40,5 @@ export const manifests: Array<ManifestTypes> = [
},
],
},
{
type: 'modal',
alias: 'Umb.Modal.ExamineFieldsSettings',
name: 'Examine Field Settings Modal',
js: () => import('./examine-management-dashboard/views/modal-views/fields-settings-modal.element.js'),
},
...examineManifests,
];