diff --git a/client/src/app/shared/components/attachment-control/attachment-control.component.html b/client/src/app/shared/components/attachment-control/attachment-control.component.html
index 65b22b0eb..609beaabf 100644
--- a/client/src/app/shared/components/attachment-control/attachment-control.component.html
+++ b/client/src/app/shared/components/attachment-control/attachment-control.component.html
@@ -1,12 +1,13 @@
-
-
+
+
+
+
diff --git a/client/src/app/shared/components/attachment-control/attachment-control.component.ts b/client/src/app/shared/components/attachment-control/attachment-control.component.ts
index 66a6e4be2..2ea306771 100644
--- a/client/src/app/shared/components/attachment-control/attachment-control.component.ts
+++ b/client/src/app/shared/components/attachment-control/attachment-control.component.ts
@@ -1,44 +1,68 @@
-import { Component, EventEmitter, Input, OnInit, Output, TemplateRef } from '@angular/core';
-import { ControlValueAccessor, FormControl } from '@angular/forms';
-import { MatDialog } from '@angular/material';
+import { FocusMonitor } from '@angular/cdk/a11y';
+import {
+ ChangeDetectionStrategy,
+ Component,
+ ElementRef,
+ EventEmitter,
+ OnInit,
+ Optional,
+ Output,
+ Self,
+ TemplateRef
+} from '@angular/core';
+import { FormBuilder, NgControl } from '@angular/forms';
+import { MatDialog, MatFormFieldControl } from '@angular/material';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { MediafileRepositoryService } from 'app/core/repositories/mediafiles/mediafile-repository.service';
+import { BaseFormControlComponent } from 'app/shared/models/base/base-form-control';
import { mediumDialogSettings } from 'app/shared/utils/dialog-settings';
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
@Component({
selector: 'os-attachment-control',
templateUrl: './attachment-control.component.html',
- styleUrls: ['./attachment-control.component.scss']
+ styleUrls: ['./attachment-control.component.scss'],
+ providers: [{ provide: MatFormFieldControl, useExisting: AttachmentControlComponent }],
+ changeDetection: ChangeDetectionStrategy.OnPush
})
-export class AttachmentControlComponent implements OnInit, ControlValueAccessor {
+export class AttachmentControlComponent extends BaseFormControlComponent implements OnInit {
/**
* Output for an error handler
*/
@Output()
public errorHandler: EventEmitter = new EventEmitter();
- /**
- * The form-control name to access the value for the form-control
- */
- @Input()
- public controlName: FormControl;
-
/**
* The file list that is necessary for the `SearchValueSelector`
*/
public mediaFileList: Observable;
+ public get empty(): boolean {
+ return !this.contentForm.value.length;
+ }
+ public get controlType(): string {
+ return 'attachment-control';
+ }
+
/**
* Default constructor
*
* @param dialogService Reference to the `MatDialog`
* @param mediaService Reference for the `MediaFileRepositoryService`
*/
- public constructor(private dialogService: MatDialog, private mediaService: MediafileRepositoryService) {}
+ public constructor(
+ fb: FormBuilder,
+ fm: FocusMonitor,
+ element: ElementRef,
+ @Optional() @Self() public ngControl: NgControl,
+ private dialogService: MatDialog,
+ private mediaService: MediafileRepositoryService
+ ) {
+ super(fb, fm, element, ngControl);
+ }
/**
* On init method
@@ -64,11 +88,9 @@ export class AttachmentControlComponent implements OnInit, ControlValueAccessor
* @param fileIDs a list with the ids of the uploaded files
*/
public uploadSuccess(fileIDs: number[]): void {
- if (this.controlName) {
- const newValues = [...this.controlName.value, ...fileIDs];
- this.controlName.setValue(newValues);
- this.dialogService.closeAll();
- }
+ const newValues = [...this.contentForm.value, ...fileIDs];
+ this.updateForm(newValues);
+ this.dialogService.closeAll();
}
/**
@@ -80,29 +102,13 @@ export class AttachmentControlComponent implements OnInit, ControlValueAccessor
this.errorHandler.emit(error);
}
- /**
- * Function to write a new value to the form.
- * Satisfy the interface.
- *
- * @param value The new value for this form.
- */
- public writeValue(value: any): void {
- if (value && this.controlName) {
- this.controlName.setValue(value);
- }
+ public onContainerClick(event: MouseEvent): void {
+ // TODO: implement
+ }
+ protected initializeForm(): void {
+ this.contentForm = this.fb.control([]);
+ }
+ protected updateForm(value: ViewMediafile[]): void {
+ this.contentForm.setValue(value);
}
-
- /**
- * Function executed when the control's value changed.
- *
- * @param fn the function that is executed.
- */
- public registerOnChange(fn: any): void {}
-
- /**
- * To satisfy the interface
- *
- * @param fn the registered callback function for onBlur-events.
- */
- public registerOnTouched(fn: any): void {}
}
diff --git a/client/src/app/shared/components/extension-field/extension-field.component.html b/client/src/app/shared/components/extension-field/extension-field.component.html
index 1c8850c5b..3e98933f8 100644
--- a/client/src/app/shared/components/extension-field/extension-field.component.html
+++ b/client/src/app/shared/components/extension-field/extension-field.component.html
@@ -38,14 +38,14 @@
(keydown)="keyDownFunction($event)"
/>
-
+
+
+
diff --git a/client/src/app/shared/components/media-upload-content/media-upload-content.component.html b/client/src/app/shared/components/media-upload-content/media-upload-content.component.html
index 4cc4a808e..3b05c2a24 100644
--- a/client/src/app/shared/components/media-upload-content/media-upload-content.component.html
+++ b/client/src/app/shared/components/media-upload-content/media-upload-content.component.html
@@ -13,16 +13,17 @@
-
-
+
+
+
+
@@ -69,14 +70,15 @@
Access groups |
-
-
+ |
+
+
+
|
diff --git a/client/src/app/shared/components/media-upload-content/media-upload-content.component.ts b/client/src/app/shared/components/media-upload-content/media-upload-content.component.ts
index 71beedb6b..3638de4e9 100644
--- a/client/src/app/shared/components/media-upload-content/media-upload-content.component.ts
+++ b/client/src/app/shared/components/media-upload-content/media-upload-content.component.ts
@@ -90,7 +90,8 @@ export class MediaUploadContentComponent implements OnInit {
public get selectedDirectoryId(): number | null {
if (this.showDirectorySelector) {
- return this.directorySelectionForm.controls.parent_id.value;
+ const parent = this.directorySelectionForm.controls.parent_id;
+ return !parent.value || typeof parent.value !== 'number' ? null : parent.value;
} else {
return this.directoryId;
}
@@ -110,7 +111,7 @@ export class MediaUploadContentComponent implements OnInit {
this.directoryBehaviorSubject = this.repo.getDirectoryBehaviorSubject();
this.groupsBehaviorSubject = this.groupRepo.getViewModelListBehaviorSubject();
this.directorySelectionForm = this.formBuilder.group({
- parent_id: []
+ parent_id: null
});
}
diff --git a/client/src/app/shared/components/search-value-selector/search-value-selector.component.html b/client/src/app/shared/components/search-value-selector/search-value-selector.component.html
index e3f019d3c..e3bb6f2cb 100644
--- a/client/src/app/shared/components/search-value-selector/search-value-selector.component.html
+++ b/client/src/app/shared/components/search-value-selector/search-value-selector.component.html
@@ -1,19 +1,12 @@
-
-
-
-
-
- {{ noneTitle | translate }}
-
-
-
-
- {{ selectedItem.getTitle() | translate }}
+
+
+
+
+ {{ noneTitle | translate }}
-
-
+
+
+
+ {{ selectedItem.getTitle() | translate }}
+
+
diff --git a/client/src/app/shared/components/search-value-selector/search-value-selector.component.spec.ts b/client/src/app/shared/components/search-value-selector/search-value-selector.component.spec.ts
index ac15cebd3..c242cf735 100644
--- a/client/src/app/shared/components/search-value-selector/search-value-selector.component.spec.ts
+++ b/client/src/app/shared/components/search-value-selector/search-value-selector.component.spec.ts
@@ -1,6 +1,6 @@
import { Component, ViewChild } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { FormBuilder, FormControl } from '@angular/forms';
+import { FormBuilder } from '@angular/forms';
import { BehaviorSubject } from 'rxjs';
@@ -43,10 +43,8 @@ describe('SearchValueSelectorComponent', () => {
hostComponent.searchValueSelectorComponent.inputListValues = subject;
const formBuilder: FormBuilder = TestBed.get(FormBuilder);
- const formGroup = formBuilder.group({
- testArray: []
- });
- hostComponent.searchValueSelectorComponent.formControl =
formGroup.get('testArray');
+ const formControl = formBuilder.control([]);
+ hostComponent.searchValueSelectorComponent.contentForm = formControl;
hostFixture.detectChanges();
expect(hostComponent.searchValueSelectorComponent).toBeTruthy();
diff --git a/client/src/app/shared/components/search-value-selector/search-value-selector.component.ts b/client/src/app/shared/components/search-value-selector/search-value-selector.component.ts
index d01428439..7b5a046a9 100644
--- a/client/src/app/shared/components/search-value-selector/search-value-selector.component.ts
+++ b/client/src/app/shared/components/search-value-selector/search-value-selector.component.ts
@@ -1,31 +1,38 @@
-import { ChangeDetectionStrategy, Component, Input, OnDestroy, ViewChild } from '@angular/core';
-import { FormControl } from '@angular/forms';
-import { MatSelect } from '@angular/material';
+import { FocusMonitor } from '@angular/cdk/a11y';
+import {
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Component,
+ ElementRef,
+ Input,
+ Optional,
+ Self
+} from '@angular/core';
+import { FormBuilder, FormControl, NgControl } from '@angular/forms';
+import { MatFormFieldControl } from '@angular/material';
import { TranslateService } from '@ngx-translate/core';
-import { Observable, Subscription } from 'rxjs';
+import { Observable } from 'rxjs';
import { auditTime } from 'rxjs/operators';
+import { BaseFormControlComponent } from 'app/shared/models/base/base-form-control';
import { Selectable } from '../selectable';
/**
- * Reusable Searchable Value Selector
+ * Searchable Value Selector
*
- * Use `multiple="true"`, `[InputListValues]=myValues`,`[formControl]="myformcontrol"` and `placeholder={{listname}}` to pass the Values and Listname
+ * Use `multiple="true"`, `[inputListValues]=myValues`,`formControlName="myformcontrol"` and `placeholder={{listname}}` to pass the Values and Listname
*
* ## Examples:
*
* ### Usage of the selector:
*
- * ngDefaultControl: https://stackoverflow.com/a/39053470
- *
* ```html
*
+ * [inputListValues]="myListValues"
+ * formControlName="myformcontrol">
*
* ```
*
@@ -35,24 +42,10 @@ import { Selectable } from '../selectable';
selector: 'os-search-value-selector',
templateUrl: './search-value-selector.component.html',
styleUrls: ['./search-value-selector.component.scss'],
+ providers: [{ provide: MatFormFieldControl, useExisting: SearchValueSelectorComponent }],
changeDetection: ChangeDetectionStrategy.OnPush
})
-export class SearchValueSelectorComponent implements OnDestroy {
- /**
- * Saves the current subscription to _inputListSubject.
- */
- private _inputListSubscription: Subscription = null;
-
- /**
- * Value of the search input
- */
- private searchValue = '';
-
- /**
- * All items
- */
- private selectableItems: Selectable[];
-
+export class SearchValueSelectorComponent extends BaseFormControlComponent {
/**
* Decide if this should be a single or multi-select-field
*/
@@ -83,55 +76,41 @@ export class SearchValueSelectorComponent implements OnDestroy {
if (!value) {
return;
}
-
- if (Array.isArray(value)) {
- this.selectableItems = value;
- } else {
- // unsubscribe to old subscription.
- if (this._inputListSubscription) {
- this._inputListSubscription.unsubscribe();
- }
- this._inputListSubscription = value.pipe(auditTime(10)).subscribe(items => {
+ this.subscriptions.push(
+ value.pipe(auditTime(10)).subscribe(items => {
this.selectableItems = items;
- if (this.formControl) {
- !!items && items.length > 0
- ? this.formControl.enable({ emitEvent: false })
- : this.formControl.disable({ emitEvent: false });
+ if (this.contentForm) {
+ this.disabled = !items || (!!items && !items.length);
}
- });
- }
+ })
+ );
}
- /**
- * Placeholder of the List
- */
- @Input()
- public listname: string;
+ public searchValue: FormControl;
+
+ public get empty(): boolean {
+ return Array.isArray(this.contentForm.value) ? !this.contentForm.value.length : !this.contentForm.value;
+ }
+
+ public controlType = 'search-value-selector';
/**
- * Name of the Form
+ * All items
*/
- @Input()
- public formControl: FormControl;
-
- /**
- * The MultiSelect Component
- */
- @ViewChild('thisSelector', { static: true })
- public thisSelector: MatSelect;
+ private selectableItems: Selectable[];
/**
* Empty constructor
*/
- public constructor(protected translate: TranslateService) {}
-
- /**
- * Unsubscribe on destroing.
- */
- public ngOnDestroy(): void {
- if (this._inputListSubscription) {
- this._inputListSubscription.unsubscribe();
- }
+ public constructor(
+ protected translate: TranslateService,
+ cd: ChangeDetectorRef,
+ fb: FormBuilder,
+ @Optional() @Self() public ngControl: NgControl,
+ fm: FocusMonitor,
+ element: ElementRef
+ ) {
+ super(fb, fm, element, ngControl);
}
/**
@@ -141,29 +120,42 @@ export class SearchValueSelectorComponent implements OnDestroy {
*/
public getFilteredItems(): Selectable[] {
if (this.selectableItems) {
+ const searchValue: string = this.searchValue.value.toLowerCase();
return this.selectableItems.filter(item => {
const idString = '' + item.id;
const foundId =
idString
.trim()
.toLowerCase()
- .indexOf(this.searchValue) !== -1;
+ .indexOf(searchValue) !== -1;
if (foundId) {
return true;
}
- const searchableString = this.translate.instant(item.getTitle()).toLowerCase();
- return searchableString.indexOf(this.searchValue) > -1;
+
+ return (
+ item
+ .toString()
+ .toLowerCase()
+ .indexOf(searchValue) > -1
+ );
});
}
}
- /**
- * Function to set the search value.
- *
- * @param searchValue the new value the user is searching for.
- */
- public onSearch(searchValue: string): void {
- this.searchValue = searchValue.toLowerCase();
+ public onContainerClick(event: MouseEvent): void {
+ if ((event.target as Element).tagName.toLowerCase() !== 'select') {
+ // this.element.nativeElement.querySelector('select').focus();
+ }
+ }
+
+ protected initializeForm(): void {
+ this.contentForm = this.fb.control([]);
+ this.searchValue = this.fb.control('');
+ }
+
+ protected updateForm(value: Selectable[] | null): void {
+ const nextValue = value;
+ this.contentForm.setValue(nextValue);
}
}
diff --git a/client/src/app/shared/models/base/base-form-control.ts b/client/src/app/shared/models/base/base-form-control.ts
new file mode 100644
index 000000000..18624e980
--- /dev/null
+++ b/client/src/app/shared/models/base/base-form-control.ts
@@ -0,0 +1,161 @@
+import { FocusMonitor } from '@angular/cdk/a11y';
+import { coerceBooleanProperty } from '@angular/cdk/coercion';
+import { ElementRef, HostBinding, Input, OnDestroy, Optional, Self } from '@angular/core';
+import { ControlValueAccessor, FormBuilder, FormControl, FormGroup, NgControl } from '@angular/forms';
+import { MatFormFieldControl } from '@angular/material';
+
+import { Subject, Subscription } from 'rxjs';
+
+/**
+ * Abstract class to implement some simple logic and provide the subclass as a controllable form-control in `MatFormField`.
+ *
+ * Please remember to prepare the `providers` in the `@Component`-decorator. Something like:
+ *
+ * ```ts
+ * @Component({
+ * selector: ...,
+ * templateUrl: ...,
+ * styleUrls: [...],
+ * providers: [{ provide: MatFormFieldControl, useExisting: }]
+ * })
+ * ```
+ */
+export abstract class BaseFormControlComponent extends MatFormFieldControl
+ implements OnDestroy, ControlValueAccessor {
+ public static nextId = 0;
+
+ @HostBinding() public id = `base-form-control-${BaseFormControlComponent.nextId++}`;
+
+ @HostBinding('class.floating') public get shouldLabelFloat(): boolean {
+ return this.focused || !this.empty;
+ }
+
+ @HostBinding('attr.aria-describedby') public describedBy = '';
+
+ @Input()
+ public set value(value: T | null) {
+ this.updateForm(value);
+ this.stateChanges.next();
+ }
+
+ public get value(): T | null {
+ return this.contentForm.value || null;
+ }
+
+ @Input()
+ public set placeholder(placeholder: string) {
+ this._placeholder = placeholder;
+ this.stateChanges.next();
+ }
+
+ public get placeholder(): string {
+ return this._placeholder;
+ }
+
+ @Input()
+ public set required(required: boolean) {
+ this._required = coerceBooleanProperty(required);
+ this.stateChanges.next();
+ }
+
+ public get required(): boolean {
+ return this._required;
+ }
+
+ @Input()
+ public set disabled(disable: boolean) {
+ this._disabled = coerceBooleanProperty(disable);
+ this._disabled ? this.contentForm.disable() : this.contentForm.enable();
+ this.stateChanges.next();
+ }
+
+ public get disabled(): boolean {
+ return this._disabled;
+ }
+
+ public abstract get empty(): boolean;
+
+ public abstract get controlType(): string;
+
+ public contentForm: FormControl | FormGroup;
+
+ public stateChanges = new Subject();
+
+ public errorState = false;
+
+ public focused = false;
+
+ private _placeholder: string;
+
+ private _required = false;
+
+ private _disabled = false;
+
+ protected subscriptions: Subscription[] = [];
+
+ public constructor(
+ protected fb: FormBuilder,
+ protected fm: FocusMonitor,
+ protected element: ElementRef,
+ @Optional() @Self() public ngControl: NgControl
+ ) {
+ super();
+
+ this.initializeForm();
+
+ if (this.ngControl !== null) {
+ this.ngControl.valueAccessor = this;
+ }
+
+ this.subscriptions.push(
+ fm.monitor(element.nativeElement, true).subscribe(origin => {
+ this.focused = !!origin;
+ this.stateChanges.next();
+ }),
+ this.contentForm.valueChanges.subscribe(nextValue => this.push(nextValue))
+ );
+ }
+
+ public ngOnDestroy(): void {
+ for (const subscription of this.subscriptions) {
+ subscription.unsubscribe();
+ }
+ this.subscriptions = [];
+
+ this.fm.stopMonitoring(this.element.nativeElement);
+
+ this.stateChanges.complete();
+ }
+
+ public writeValue(value: T): void {
+ this.value = value;
+ }
+ public registerOnChange(fn: any): void {
+ this._onChange = fn;
+ }
+ public registerOnTouched(fn: any): void {
+ this._onTouched = fn;
+ }
+ public setDisabledState?(isDisabled: boolean): void {
+ this.disabled = isDisabled;
+ }
+
+ public setDescribedByIds(ids: string[]): void {
+ this.describedBy = ids.join(' ');
+ }
+
+ public abstract onContainerClick(event: MouseEvent): void;
+
+ protected _onChange = (value: T) => {};
+
+ protected _onTouched = (value: T) => {};
+
+ protected abstract initializeForm(): void;
+
+ protected abstract updateForm(value: T | null): void;
+
+ protected push(value: T): void {
+ this._onChange(value);
+ this._onTouched(value);
+ }
+}
diff --git a/client/src/app/shared/models/motions/motion-poll.ts b/client/src/app/shared/models/motions/motion-poll.ts
index 01dd6287e..f9991450c 100644
--- a/client/src/app/shared/models/motions/motion-poll.ts
+++ b/client/src/app/shared/models/motions/motion-poll.ts
@@ -1,14 +1,18 @@
import { BasePoll, BasePollWithoutNestedModels } from '../poll/base-poll';
import { MotionOption } from './motion-option';
-export enum MotionPollmethods {
- 'YN' = 'YN',
- 'YNA' = 'YNA'
+export enum MotionPollMethods {
+ YN = 'YN',
+ YNA = 'YNA'
}
+export const MotionPollMethodsVerbose = {
+ YN: 'Yes/No',
+ YNA: 'Yes/No/Abstain'
+};
export interface MotionPollWithoutNestedModels extends BasePollWithoutNestedModels {
motion_id: number;
- pollmethod: MotionPollmethods;
+ pollmethod: MotionPollMethods;
}
/**
@@ -22,5 +26,9 @@ export class MotionPoll extends BasePoll {
public constructor(input?: any) {
super(MotionPoll.COLLECTIONSTRING, input);
}
+
+ public get pollmethodVerbose(): string {
+ return MotionPollMethodsVerbose[this.pollmethod];
+ }
}
export interface MotionPoll extends MotionPollWithoutNestedModels {}
diff --git a/client/src/app/shared/models/poll/base-poll.ts b/client/src/app/shared/models/poll/base-poll.ts
index 1fc1fe6fe..f5cb70763 100644
--- a/client/src/app/shared/models/poll/base-poll.ts
+++ b/client/src/app/shared/models/poll/base-poll.ts
@@ -8,12 +8,55 @@ export enum PollState {
Published
}
+export const PollStateVerbose = {
+ 1: 'Created',
+ 2: 'Started',
+ 3: 'Finished',
+ 4: 'Published'
+};
+
export enum PollType {
Analog = 'analog',
Named = 'named',
Pseudoanonymous = 'pseudoanonymous'
}
+export const PollTypeVerbose = {
+ analog: 'Analog',
+ named: 'Named',
+ pseudoanonymous: 'Pseudoanonymous'
+};
+
+export enum PercentBase {
+ YN = 'YN',
+ YNA = 'YNA',
+ Valid = 'valid',
+ Cast = 'cast',
+ Disabled = 'disabled'
+}
+
+export const PercentBaseVerbose = {
+ YN: 'Yes/No',
+ YNA: 'Yes/No/Abstain',
+ valid: 'Valid votes',
+ cast: 'Casted votes',
+ disabled: 'Disabled'
+};
+
+export enum MajorityMethod {
+ Simple = 'simple',
+ TwoThirds = 'two_thirds',
+ ThreeQuarters = 'three_quarters',
+ Disabled = 'disabled'
+}
+
+export const MajorityMethodVerbose = {
+ simple: 'Simple',
+ two_thirds: 'Two Thirds',
+ three_quarters: 'Three Quarters',
+ disabled: 'Disabled'
+};
+
export interface BasePollWithoutNestedModels {
state: PollState;
type: PollType;
@@ -23,6 +66,8 @@ export interface BasePollWithoutNestedModels {
votescast: number;
groups_id: number[];
voted_id: number[];
+ majority_method: MajorityMethod;
+ onehundred_percent_base: PercentBase;
}
export abstract class BasePoll> extends BaseDecimalModel {
@@ -31,5 +76,21 @@ export abstract class BasePoll> extends BaseDecimal
protected getDecimalFields(): (keyof BasePoll)[] {
return ['votesvalid', 'votesinvalid', 'votescast'];
}
+
+ public get stateVerbose(): string {
+ return PollStateVerbose[this.state];
+ }
+
+ public get typeVerbose(): string {
+ return PollTypeVerbose[this.type];
+ }
+
+ public get majorityMethodVerbose(): string {
+ return MajorityMethodVerbose[this.majority_method];
+ }
+
+ public get percentBaseVerbose(): string {
+ return PercentBaseVerbose[this.onehundred_percent_base];
+ }
}
export interface BasePoll> extends BasePollWithoutNestedModels {}
diff --git a/client/src/app/site/agenda/components/list-of-speakers/list-of-speakers.component.html b/client/src/app/site/agenda/components/list-of-speakers/list-of-speakers.component.html
index 81ac75268..aac27ad5c 100644
--- a/client/src/app/site/agenda/components/list-of-speakers/list-of-speakers.component.html
+++ b/client/src/app/site/agenda/components/list-of-speakers/list-of-speakers.component.html
@@ -1,4 +1,10 @@
-
+
@@ -8,7 +14,15 @@
@@ -77,12 +91,7 @@
0">
-
+
@@ -124,13 +133,14 @@
diff --git a/client/src/app/site/assignments/components/assignment-detail/assignment-detail.component.html b/client/src/app/site/assignments/components/assignment-detail/assignment-detail.component.html
index 8ab276b7a..563566f66 100644
--- a/client/src/app/site/assignments/components/assignment-detail/assignment-detail.component.html
+++ b/client/src/app/site/assignments/components/assignment-detail/assignment-detail.component.html
@@ -145,10 +145,7 @@
*ngIf="assignment && assignment.polls && assignment.polls.length"
>
-
+
@@ -194,14 +191,15 @@
*ngIf="hasPerms('addOthers') && filteredCandidates && filteredCandidates.value.length > 0"
[formGroup]="candidatesForm"
>
-
+
+
+
@@ -238,11 +236,7 @@
-
+
{{ 'The title is required' | translate }}
@@ -256,22 +250,20 @@
>
-
-
-
+
+
+
+
-
-
diff --git a/client/src/app/site/history/components/history-list/history-list.component.html b/client/src/app/site/history/components/history-list/history-list.component.html
index f92137f09..897133b57 100644
--- a/client/src/app/site/history/components/history-list/history-list.component.html
+++ b/client/src/app/site/history/components/history-list/history-list.component.html
@@ -13,15 +13,16 @@
@@ -142,13 +132,14 @@
-
+
+
+