Merge pull request #890 from umbraco/feature/dictionary-update

This commit is contained in:
Jacob Overgaard
2023-10-05 13:01:37 +02:00
committed by GitHub
7 changed files with 83 additions and 84 deletions

View File

@@ -1,5 +1,6 @@
const { rest } = window.MockServiceWorker;
import { umbDictionaryData } from '../data/dictionary.data.js';
import { umbracoPath } from '@umbraco-cms/backoffice/utils';
import {
ImportDictionaryRequestModel,
DictionaryOverviewResponseModel,
@@ -48,7 +49,7 @@ const overviewData: Array<DictionaryOverviewResponseModel> = [
// TODO: add schema
export const handlers = [
rest.get('/umbraco/management/api/v1/dictionary/:id', (req, res, ctx) => {
rest.get(umbracoPath('/dictionary/:id'), (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
@@ -56,7 +57,7 @@ export const handlers = [
return res(ctx.status(200), ctx.json(dictionary));
}),
rest.get('/umbraco/management/api/v1/dictionary', (req, res, ctx) => {
rest.get(umbracoPath('/dictionary'), (req, res, ctx) => {
const skip = req.url.searchParams.get('skip');
const take = req.url.searchParams.get('take');
if (!skip || !take) return;
@@ -74,25 +75,16 @@ export const handlers = [
return res(ctx.status(200), ctx.json(response));
}),
rest.post('/umbraco/management/api/v1/dictionary', async (req, res, ctx) => {
rest.post(umbracoPath('/dictionary'), async (req, res, ctx) => {
const data = await req.json();
if (!data) return;
data.icon = 'umb:book-alt';
data.hasChildren = false;
data.type = 'dictionary-item';
data.translations = [
{
isoCode: 'en-US',
translation: '',
},
{
isoCode: 'fr',
translation: '',
},
];
const value = umbDictionaryData.save(data.id, data);
const value = umbDictionaryData.insert(data);
const createdResult = {
value,
@@ -102,7 +94,7 @@ export const handlers = [
return res(ctx.status(200), ctx.json(createdResult));
}),
rest.patch('/umbraco/management/api/v1/dictionary/:id', async (req, res, ctx) => {
rest.patch(umbracoPath('/dictionary/:id'), async (req, res, ctx) => {
const data = await req.json();
if (!data) return;
@@ -115,7 +107,19 @@ export const handlers = [
return res(ctx.status(200), ctx.json(saved));
}),
rest.get('/umbraco/management/api/v1/tree/dictionary/root', (req, res, ctx) => {
rest.put(umbracoPath('/dictionary/:id'), async (req, res, ctx) => {
const data = await req.json();
if (!data) return;
const id = req.params.id as string;
if (!id) return;
const saved = umbDictionaryData.save(id, data);
return res(ctx.status(200), ctx.json(saved));
}),
rest.get(umbracoPath('/tree/dictionary/root'), (req, res, ctx) => {
const items = umbDictionaryData.getTreeRoot();
const response = {
total: items.length,
@@ -124,7 +128,7 @@ export const handlers = [
return res(ctx.status(200), ctx.json(response));
}),
rest.get('/umbraco/management/api/v1/tree/dictionary/children', (req, res, ctx) => {
rest.get(umbracoPath('/tree/dictionary/children'), (req, res, ctx) => {
const parentId = req.url.searchParams.get('parentId');
if (!parentId) return;
@@ -138,7 +142,7 @@ export const handlers = [
return res(ctx.status(200), ctx.json(response));
}),
rest.get('/umbraco/management/api/v1/tree/dictionary/item', (req, res, ctx) => {
rest.get(umbracoPath('/tree/dictionary/item'), (req, res, ctx) => {
const ids = req.url.searchParams.getAll('id');
if (!ids) return;
@@ -147,7 +151,7 @@ export const handlers = [
return res(ctx.status(200), ctx.json(items));
}),
rest.delete('/umbraco/management/api/v1/dictionary/:id', (req, res, ctx) => {
rest.delete(umbracoPath('/dictionary/:id'), (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
@@ -157,7 +161,7 @@ export const handlers = [
}),
// TODO => handle properly, querystring breaks handler
rest.get('/umbraco/management/api/v1/dictionary/:id/export', (req, res, ctx) => {
rest.get(umbracoPath('/dictionary/:id/export'), (req, res, ctx) => {
const id = req.params.id as string;
if (!id) return;
@@ -170,13 +174,13 @@ export const handlers = [
return res(ctx.status(200));
}),
rest.post('/umbraco/management/api/v1/dictionary/upload', async (req, res, ctx) => {
rest.post(umbracoPath('/dictionary/upload'), async (req, res, ctx) => {
if (!req.arrayBuffer()) return;
return res(ctx.status(200), ctx.json(uploadResponse));
}),
rest.post('/umbraco/management/api/v1/dictionary/import', async (req, res, ctx) => {
rest.post(umbracoPath('/dictionary/import'), async (req, res, ctx) => {
const file = req.url.searchParams.get('file');
if (!file || !importResponse.id) return;

View File

@@ -1,15 +1,9 @@
import { UmbDictionaryRepository } from '../../dictionary/repository/dictionary.repository.js';
import { UmbTextStyles } from "@umbraco-cms/backoffice/style";
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { css, html, customElement, state, when } from '@umbraco-cms/backoffice/external/lit';
import { UmbTableConfig, UmbTableColumn, UmbTableItem } from '@umbraco-cms/backoffice/components';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { DictionaryOverviewResponseModel, LanguageResponseModel } from '@umbraco-cms/backoffice/backend-api';
import {
UmbModalManagerContext,
UMB_MODAL_MANAGER_CONTEXT_TOKEN,
UMB_CREATE_DICTIONARY_MODAL,
} from '@umbraco-cms/backoffice/modal';
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
@customElement('umb-dashboard-translation-dictionary')
export class UmbDashboardTranslationDictionaryElement extends UmbLitElement {
@@ -25,8 +19,6 @@ export class UmbDashboardTranslationDictionaryElement extends UmbLitElement {
#repo!: UmbDictionaryRepository;
#modalContext!: UmbModalManagerContext;
#tableItems: Array<UmbTableItem> = [];
#tableColumns: Array<UmbTableColumn> = [];
@@ -35,10 +27,6 @@ export class UmbDashboardTranslationDictionaryElement extends UmbLitElement {
constructor() {
super();
new UmbContextConsumerController(this, UMB_MODAL_MANAGER_CONTEXT_TOKEN, (instance) => {
this.#modalContext = instance;
});
}
async connectedCallback() {
@@ -66,7 +54,7 @@ export class UmbDashboardTranslationDictionaryElement extends UmbLitElement {
#setTableColumns() {
this.#tableColumns = [
{
name: 'Name',
name: this.localize.term('general_name'),
alias: 'name',
},
];
@@ -92,7 +80,7 @@ export class UmbDashboardTranslationDictionaryElement extends UmbLitElement {
columnAlias: 'name',
value: html`<a
style="font-weight:bold"
href="/section/dictionary/workspace/dictionary-item/edit/${dictionary.id}">
href="section/dictionary/workspace/dictionary-item/edit/${dictionary.id}">
${dictionary.name}</a
> `,
},
@@ -107,11 +95,11 @@ export class UmbDashboardTranslationDictionaryElement extends UmbLitElement {
value: dictionary.translatedIsoCodes?.includes(l.isoCode)
? html`<uui-icon
name="check"
title="Translation exists for ${l.name}"
title="${this.localize.term('visuallyHiddenTexts_hasTranslation')} (${l.name})"
style="color:var(--uui-color-positive-standalone);display:inline-block"></uui-icon>`
: html`<uui-icon
name="alert"
title="Translation does not exist for ${l.name}"
title="${this.localize.term('visuallyHiddenTexts_noTranslation')} (${l.name})"
style="color:var(--uui-color-danger-standalone);display:inline-block"></uui-icon>`,
});
});
@@ -123,41 +111,27 @@ export class UmbDashboardTranslationDictionaryElement extends UmbLitElement {
}
#filter(e: { target: HTMLInputElement }) {
this._tableItemsFiltered = e.target.value
? this.#tableItems.filter((t) => t.id.includes(e.target.value))
const searchValue = e.target.value.toLocaleLowerCase();
this._tableItemsFiltered = searchValue
? this.#tableItems.filter((t) => t.id.toLocaleLowerCase().includes(searchValue))
: this.#tableItems;
}
async #create() {
// TODO: what to do if modal service is not available?
if (!this.#modalContext) return;
if (!this.#repo) return;
const modalContext = this.#modalContext?.open(UMB_CREATE_DICTIONARY_MODAL, { parentId: null });
const { name, parentId } = await modalContext.onSubmit();
if (!name || parentId === undefined) return;
const { data: url } = await this.#repo.create({ name, parentId });
if (!url) return;
//TODO: Why do we need to extract the id like this?
const id = url.substring(url.lastIndexOf('/') + 1);
history.pushState({}, '', `/section/dictionary/workspace/dictionary-item/edit/${id}`);
}
render() {
return html`
<umb-body-layout header-transparent>
<div id="header" slot="header">
<uui-button type="button" look="outline" label="Create dictionary item" @click=${this.#create}
>Create dictionary item</uui-button
>
<uui-button
type="button"
look="outline"
label=${this.localize.term('dictionary_createNew')}
href="section/dictionary/workspace/dictionary-item/create/null">
${this.localize.term('dictionary_createNew')}
</uui-button>
<uui-input
@keyup="${this.#filter}"
placeholder="Type to filter..."
label="Type to filter dictionary"
placeholder=${this.localize.term('placeholders_filter')}
label=${this.localize.term('placeholders_filter')}
id="searchbar">
<div slot="prepend">
<uui-icon name="search" id="searchbar_icon"></uui-icon>
@@ -166,11 +140,12 @@ export class UmbDashboardTranslationDictionaryElement extends UmbLitElement {
</div>
${when(
this._tableItemsFiltered.length,
() => html` <umb-table
.config=${this._tableConfig}
.columns=${this.#tableColumns}
.items=${this._tableItemsFiltered}></umb-table>`,
() => html`<umb-empty-state>There were no dictionary items found.</umb-empty-state>`
() =>
html` <umb-table
.config=${this._tableConfig}
.columns=${this.#tableColumns}
.items=${this._tableItemsFiltered}></umb-table>`,
() => html`<umb-empty-state>${this.localize.term('emptyStates_emptyDictionaryTree')}</umb-empty-state>`,
)}
</umb-body-layout>
`;

View File

@@ -114,7 +114,7 @@ export class UmbDictionaryDetailServerDataSource
// TODO => parentId will be a guid param once #13786 is merged and API regenerated
return await tryExecuteAndNotify(
this.#host,
DictionaryResource.postDictionaryImport({ requestBody: { temporaryFileId, parentId } })
DictionaryResource.postDictionaryImport({ requestBody: { temporaryFileId, parentId } }),
);
}
@@ -129,7 +129,7 @@ export class UmbDictionaryDetailServerDataSource
this.#host,
DictionaryResource.postDictionaryImport({
requestBody: formData,
})
}),
);
}

View File

@@ -38,10 +38,13 @@ export class UmbDictionaryWorkspaceEditorElement extends UmbLitElement {
return html`
<umb-workspace-editor alias="Umb.Workspace.Dictionary">
<div id="header" slot="header">
<uui-button href="/section/dictionary/dashboard" label="Back to list" compact>
<uui-button href="section/dictionary/dashboard" label=${this.localize.term('general_backToOverview')} compact>
<uui-icon name="umb:arrow-left"></uui-icon>
</uui-button>
<uui-input .value=${this._name} @input="${this.#handleInput}" label="Dictionary name"></uui-input>
<uui-input
.value=${this._name ?? ''}
@input="${this.#handleInput}"
label="${this.localize.term('general_dictionary')} ${this.localize.term('general_name')}"></uui-input>
</div>
</umb-workspace-editor>
`;

View File

@@ -76,8 +76,16 @@ export class UmbDictionaryWorkspaceContext
async save() {
if (!this.#data.value) return;
if (!this.#data.value.id) return;
await this.repository.save(this.#data.value.id, this.#data.value);
this.setIsNew(false);
if (this.getIsNew()) {
await this.repository.create(this.#data.value);
this.setIsNew(false);
} else {
await this.repository.save(this.#data.value.id, this.#data.value);
}
const data = this.getData();
if (data) this.saveComplete(data);
}
public destroy(): void {
@@ -85,8 +93,10 @@ export class UmbDictionaryWorkspaceContext
}
}
export const UMB_DICTIONARY_WORKSPACE_CONTEXT = new UmbContextToken<UmbSaveableWorkspaceContextInterface, UmbDictionaryWorkspaceContext>(
export const UMB_DICTIONARY_WORKSPACE_CONTEXT = new UmbContextToken<
UmbSaveableWorkspaceContextInterface,
UmbDictionaryWorkspaceContext
>(
'UmbWorkspaceContext',
(context): context is UmbDictionaryWorkspaceContext => context.getEntityType?.() === 'dictionary-item'
(context): context is UmbDictionaryWorkspaceContext => context.getEntityType?.() === 'dictionary-item',
);

View File

@@ -1,6 +1,6 @@
import { UmbDictionaryWorkspaceContext } from './dictionary-workspace.context.js';
import { UmbDictionaryWorkspaceEditorElement } from './dictionary-workspace-editor.element.js';
import { UmbTextStyles } from "@umbraco-cms/backoffice/style";
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { html, customElement, state } from '@umbraco-cms/backoffice/external/lit';
import type { UmbRoute } from '@umbraco-cms/backoffice/router';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
@@ -20,6 +20,14 @@ export class UmbWorkspaceDictionaryElement extends UmbLitElement {
this.#workspaceContext.load(id);
},
},
{
path: 'create/:parentId',
component: () => this.#element,
setup: (_component, info) => {
const parentId = info.match.params.parentId === 'null' ? null : info.match.params.parentId;
this.#workspaceContext.create(parentId);
},
},
];
render() {

View File

@@ -1,7 +1,7 @@
import { UMB_DICTIONARY_WORKSPACE_CONTEXT } from '../../dictionary-workspace.context.js';
import { UmbDictionaryRepository } from '../../../repository/dictionary.repository.js';
import { UUITextareaElement, UUITextareaEvent } from '@umbraco-cms/backoffice/external/uui';
import { css, html, customElement, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit';
import { css, html, customElement, state, repeat, ifDefined, unsafeHTML } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { DictionaryItemResponseModel, LanguageResponseModel } from '@umbraco-cms/backoffice/backend-api';
@customElement('umb-workspace-view-dictionary-editor')
@@ -62,12 +62,11 @@ export class UmbWorkspaceViewDictionaryEditorElement extends UmbLitElement {
render() {
return html`
<uui-box>
<p>Edit the different language versions for the dictionary item '<em>${this._dictionary?.name}</em>' below.</p>
${unsafeHTML(this.localize.term('dictionaryItem_description', this._dictionary?.name || 'unnamed'))}
${repeat(
this._languages,
(item) => item.isoCode,
(item) => this.#renderTranslation(item)
(item) => this.#renderTranslation(item),
)}
</uui-box>
`;