diff --git a/client/src/app/core/translate/translation-parser.ts b/client/src/app/core/translate/translation-parser.ts index 9beeadaab..f8215248c 100644 --- a/client/src/app/core/translate/translation-parser.ts +++ b/client/src/app/core/translate/translation-parser.ts @@ -2,12 +2,12 @@ import { TranslateDefaultParser, TranslateStore } from '@ngx-translate/core'; import { ConfigService } from '../services/config.service'; import { Injectable } from '@angular/core'; -interface CustomTranslation { +export interface CustomTranslation { original: string; translation: string; } -type CustomTranslations = CustomTranslation[]; +export type CustomTranslations = CustomTranslation[]; /** * Custom translate parser. Intercepts and use custom translations from the configservice. diff --git a/client/src/app/site/config/components/config-field/config-field.component.html b/client/src/app/site/config/components/config-field/config-field.component.html index f0bb07ce2..c7ad43af0 100644 --- a/client/src/app/site/config/components/config-field/config-field.component.html +++ b/client/src/app/site/config/components/config-field/config-field.component.html @@ -69,6 +69,11 @@

{{ configItem.label | translate }}

+ + +
+ +
diff --git a/client/src/app/site/config/components/config-field/config-field.component.ts b/client/src/app/site/config/components/config-field/config-field.component.ts index e04d9e63f..ba5a661ee 100644 --- a/client/src/app/site/config/components/config-field/config-field.component.ts +++ b/client/src/app/site/config/components/config-field/config-field.component.ts @@ -54,7 +54,7 @@ export class ConfigFieldComponent extends BaseComponent implements OnInit { */ @Input() public set item(value: ViewConfig) { - if (value.hasConstantsInfo) { + if (value && value.hasConstantsInfo) { this.configItem = value; if (this.form) { @@ -155,10 +155,7 @@ export class ConfigFieldComponent extends BaseComponent implements OnInit { if (this.configItem.inputType === 'datetimepicker') { value = Date.parse(value); } - // TODO: Solve this via a custom input form. - if (this.configItem.inputType === 'translations') { - value = JSON.parse(value); - } + this.debounceTimeout = null; this.repo.update({ value: value }, this.configItem).then(() => { this.error = null; @@ -215,7 +212,7 @@ export class ConfigFieldComponent extends BaseComponent implements OnInit { * @returns wheather it should be excluded or not */ public isExcludedType(type: string): boolean { - const excluded = ['boolean', 'markupText', 'text']; + const excluded = ['boolean', 'markupText', 'text', 'translations']; return excluded.includes(type); } } diff --git a/client/src/app/site/config/components/config-list/config-list.component.spec.ts b/client/src/app/site/config/components/config-list/config-list.component.spec.ts index 14a13fee2..24f611883 100644 --- a/client/src/app/site/config/components/config-list/config-list.component.spec.ts +++ b/client/src/app/site/config/components/config-list/config-list.component.spec.ts @@ -3,6 +3,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { E2EImportsModule } from '../../../../../e2e-imports.module'; import { ConfigListComponent } from './config-list.component'; import { ConfigFieldComponent } from '../config-field/config-field.component'; +import { CustomTranslationComponent } from '../custom-translation/custom-translation.component'; describe('ConfigListComponent', () => { let component: ConfigListComponent; @@ -11,7 +12,7 @@ describe('ConfigListComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [E2EImportsModule], - declarations: [ConfigListComponent, ConfigFieldComponent] + declarations: [ConfigListComponent, ConfigFieldComponent, CustomTranslationComponent] }).compileComponents(); })); diff --git a/client/src/app/site/config/components/custom-translation/custom-translation.component.html b/client/src/app/site/config/components/custom-translation/custom-translation.component.html new file mode 100644 index 000000000..031522523 --- /dev/null +++ b/client/src/app/site/config/components/custom-translation/custom-translation.component.html @@ -0,0 +1,18 @@ + +
+
+ + + + arrow_forward + + + + +
+
+ + + diff --git a/client/src/app/site/config/components/custom-translation/custom-translation.component.scss b/client/src/app/site/config/components/custom-translation/custom-translation.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/client/src/app/site/config/components/custom-translation/custom-translation.component.spec.ts b/client/src/app/site/config/components/custom-translation/custom-translation.component.spec.ts new file mode 100644 index 000000000..d48e687d6 --- /dev/null +++ b/client/src/app/site/config/components/custom-translation/custom-translation.component.spec.ts @@ -0,0 +1,26 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CustomTranslationComponent } from './custom-translation.component'; +import { E2EImportsModule } from 'e2e-imports.module'; + +describe('CustomTranslationComponent', () => { + let component: CustomTranslationComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [E2EImportsModule], + declarations: [CustomTranslationComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CustomTranslationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/site/config/components/custom-translation/custom-translation.component.ts b/client/src/app/site/config/components/custom-translation/custom-translation.component.ts new file mode 100644 index 000000000..9cbd8e661 --- /dev/null +++ b/client/src/app/site/config/components/custom-translation/custom-translation.component.ts @@ -0,0 +1,118 @@ +import { Component, forwardRef } from '@angular/core'; + +import { CustomTranslation, CustomTranslations } from 'app/core/translate/translation-parser'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; + +/** + * Custom translations as custom form component + * + * @example: + * ```html + * + * ``` + */ +@Component({ + selector: 'os-custom-translation', + templateUrl: './custom-translation.component.html', + styleUrls: ['./custom-translation.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => CustomTranslationComponent), + multi: true + } + ] +}) +export class CustomTranslationComponent implements ControlValueAccessor { + /** + * Holds the custom translations in a list + */ + public translations: CustomTranslations = []; + + /** + * Empty constructor + */ + public constructor() {} + + /** + * Helper function to determine which information to give to the parent form + */ + private propagateChange = (_: any) => {}; + + /** + * The value from the FormControl + * + * @param obj the value from the parent form. Type "any" is required by the interface + */ + public writeValue(obj: any): void { + if (obj) { + this.translations = obj; + } + } + + /** + * Hands changes back to the parent form + * + * @param fn the function to propagate the changes + */ + public registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + /** + * To satisfy the interface. + * + * @param fn + */ + public registerOnTouched(fn: any): void {} + + /** + * To satisfy the interface + * + * @param isDisabled + */ + public setDisabledState?(isDisabled: boolean): void {} + + /** + * Detects changes to the "original" word + * + * @param value the value that was typed + * @param index the index of the change + */ + public onChangeOriginal(value: string, index: number): void { + this.translations[index].original = value; + this.propagateChange(this.translations); + } + + /** + * Detects changes to the translation + * @param value the value that was typed + * @param index the index of the change + */ + public onChangeTranslation(value: string, index: number): void { + this.translations[index].translation = value; + this.propagateChange(this.translations); + } + + /** + * Removes a custom translation + * @param index the translation to remove + */ + public onRemoveTranslation(index: number): void { + this.translations.splice(index, 1); + this.propagateChange(this.translations); + } + + /** + * Adds a new custom translation to the list and to the server + */ + public onAddNewTranslation(): void { + const newCustomTranslation: CustomTranslation = { + original: 'New', + translation: 'New' + }; + + this.translations.push(newCustomTranslation); + this.propagateChange(this.translations); + } +} diff --git a/client/src/app/site/config/config.module.ts b/client/src/app/site/config/config.module.ts index 2230cba92..2d935b995 100644 --- a/client/src/app/site/config/config.module.ts +++ b/client/src/app/site/config/config.module.ts @@ -4,9 +4,11 @@ import { SharedModule } from '../../shared/shared.module'; import { ConfigRoutingModule } from './config-routing.module'; import { ConfigListComponent } from './components/config-list/config-list.component'; import { ConfigFieldComponent } from './components/config-field/config-field.component'; +import { CustomTranslationComponent } from './components/custom-translation/custom-translation.component'; @NgModule({ imports: [CommonModule, ConfigRoutingModule, SharedModule], - declarations: [ConfigListComponent, ConfigFieldComponent] + declarations: [ConfigListComponent, ConfigFieldComponent, CustomTranslationComponent], + entryComponents: [CustomTranslationComponent] }) export class ConfigModule {} diff --git a/client/src/app/site/config/models/view-config.ts b/client/src/app/site/config/models/view-config.ts index a41758125..508e9c1f1 100644 --- a/client/src/app/site/config/models/view-config.ts +++ b/client/src/app/site/config/models/view-config.ts @@ -112,7 +112,9 @@ export class ViewConfig extends BaseViewModel { * like the type="color" input... */ public getDebouncingTimeout(): number { - if (this.inputType === 'string' || this.inputType === 'text' || this.inputType === 'markupText') { + if (this.inputType === 'markupText' || this.inputType === 'translations') { + return 2500; + } else if (this.inputType === 'string' || this.inputType === 'text') { return 1000; } else { return 100;