diff --git a/client/src/app/shared/validators/custom-validators.ts b/client/src/app/shared/validators/custom-validators.ts index 5bb4e8f73..6abba7194 100644 --- a/client/src/app/shared/validators/custom-validators.ts +++ b/client/src/app/shared/validators/custom-validators.ts @@ -1,4 +1,4 @@ -import { FormGroup, ValidationErrors, ValidatorFn } from '@angular/forms'; +import { AbstractControl, FormGroup, ValidationErrors, ValidatorFn } from '@angular/forms'; /** * Constant to validate a `duration` field. @@ -17,3 +17,13 @@ export const durationValidator: ValidatorFn = (control: FormGroup): ValidationEr const regExp = /^\s*([0-9]+)(:)?([0-5][0-9]?)?\s*[h|m]?$/g; return regExp.test(control.value) || control.value === '' ? null : { valid: false }; }; + +export function isNumberRange(minCtrlName: string, maxCtrlName: string): ValidatorFn { + return (formControl: AbstractControl): { [key: string]: any } => { + const min = formControl.get(minCtrlName).value; + const max = formControl.get(maxCtrlName).value; + if (min > max) { + return { rangeError: true }; + } + }; +} diff --git a/client/src/app/site/assignments/modules/assignment-poll/components/assignment-poll-meta-info/assignment-poll-meta-info.component.html b/client/src/app/site/assignments/modules/assignment-poll/components/assignment-poll-meta-info/assignment-poll-meta-info.component.html index 7432960d7..4a5544d9e 100644 --- a/client/src/app/site/assignments/modules/assignment-poll/components/assignment-poll-meta-info/assignment-poll-meta-info.component.html +++ b/client/src/app/site/assignments/modules/assignment-poll/components/assignment-poll-meta-info/assignment-poll-meta-info.component.html @@ -40,10 +40,10 @@ - {{ pollPropertyVerbose.max_votes_amount | translate }}: {{ poll.max_votes_amount }} -
{{ pollPropertyVerbose.min_votes_amount | translate }}: {{ poll.min_votes_amount }}
+ {{ pollPropertyVerbose.max_votes_amount | translate }}: {{ poll.max_votes_amount }} +
diff --git a/client/src/app/site/assignments/modules/assignment-poll/components/assignment-poll-meta-info/assignment-poll-meta-info.component.ts b/client/src/app/site/assignments/modules/assignment-poll/components/assignment-poll-meta-info/assignment-poll-meta-info.component.ts index 3533ca617..e2fa47906 100644 --- a/client/src/app/site/assignments/modules/assignment-poll/components/assignment-poll-meta-info/assignment-poll-meta-info.component.ts +++ b/client/src/app/site/assignments/modules/assignment-poll/components/assignment-poll-meta-info/assignment-poll-meta-info.component.ts @@ -27,8 +27,7 @@ export class AssignmentPollMetaInfoComponent { public constructor() {} public userCanVoe(): boolean { - // this.poll.canBeVotedFor - return true; + return this.poll.canBeVotedFor(); } public getOptionTitle(option: ViewAssignmentOption): string { diff --git a/client/src/app/site/assignments/modules/assignment-poll/components/assignment-poll-vote/assignment-poll-vote.component.html b/client/src/app/site/assignments/modules/assignment-poll/components/assignment-poll-vote/assignment-poll-vote.component.html index 9d3dbc9c0..92b31d4f6 100644 --- a/client/src/app/site/assignments/modules/assignment-poll/components/assignment-poll-vote/assignment-poll-vote.component.html +++ b/client/src/app/site/assignments/modules/assignment-poll/components/assignment-poll-vote/assignment-poll-vote.component.html @@ -31,6 +31,11 @@

{{ 'Available votes' | translate }}: {{ getVotesAvailable(delegation) }}/{{ poll.max_votes_amount }} + + + ({{ 'At least' | translate }} {{ poll.min_votes_amount }}) +

@@ -169,7 +174,7 @@ mat-flat-button color="accent" (click)="submitVote(delegation)" - [disabled]="getVotesCount(delegation) == 0" + [disabled]="getVotesCount(delegation) < minVotes" > how_to_vote diff --git a/client/src/app/site/assignments/modules/assignment-poll/components/assignment-poll-vote/assignment-poll-vote.component.ts b/client/src/app/site/assignments/modules/assignment-poll/components/assignment-poll-vote/assignment-poll-vote.component.ts index 333909319..153429713 100644 --- a/client/src/app/site/assignments/modules/assignment-poll/components/assignment-poll-vote/assignment-poll-vote.component.ts +++ b/client/src/app/site/assignments/modules/assignment-poll/components/assignment-poll-vote/assignment-poll-vote.component.ts @@ -57,6 +57,10 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponentDirective< return this.poll.assignment.default_poll_description; } + public get minVotes(): number { + return this.poll.min_votes_amount; + } + public constructor( title: Title, protected translate: TranslateService, diff --git a/client/src/app/site/polls/components/base-poll-dialog.component.ts b/client/src/app/site/polls/components/base-poll-dialog.component.ts index 2d5d223b8..cbc4fc129 100644 --- a/client/src/app/site/polls/components/base-poll-dialog.component.ts +++ b/client/src/app/site/polls/components/base-poll-dialog.component.ts @@ -58,6 +58,8 @@ export abstract class BasePollDialogComponent< votes: this.getVoteData(), publish_immediately: this.publishImmediately }; + console.log('answer: ', answer); + this.dialogRef.close(answer); } diff --git a/client/src/app/site/polls/components/poll-form/poll-form.component.html b/client/src/app/site/polls/components/poll-form/poll-form.component.html index 14aeafebb..d10c666dd 100644 --- a/client/src/app/site/polls/components/poll-form/poll-form.component.html +++ b/client/src/app/site/polls/components/poll-form/poll-form.component.html @@ -30,7 +30,7 @@ - + {{ 'This field is required.' | translate }} - - - - + + + + + + {{ 'Min votes must be smaller or equal to max votes' | translate }} + + - - - - + + + + + -
+
{{ PollPropertyVerbose.global_yes | translate }} diff --git a/client/src/app/site/polls/components/poll-form/poll-form.component.ts b/client/src/app/site/polls/components/poll-form/poll-form.component.ts index 64aa00509..66069b0e3 100644 --- a/client/src/app/site/polls/components/poll-form/poll-form.component.ts +++ b/client/src/app/site/polls/components/poll-form/poll-form.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Title } from '@angular/platform-browser'; @@ -13,7 +13,9 @@ import { VotingPrivacyWarningComponent } from 'app/shared/components/voting-priv import { AssignmentPollMethod, AssignmentPollPercentBase } from 'app/shared/models/assignments/assignment-poll'; import { PercentBase } from 'app/shared/models/poll/base-poll'; import { PollType } from 'app/shared/models/poll/base-poll'; +import { ParentErrorStateMatcher } from 'app/shared/parent-error-state-matcher'; import { infoDialogSettings } from 'app/shared/utils/dialog-settings'; +import { isNumberRange } from 'app/shared/validators/custom-validators'; import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll'; import { BaseViewComponentDirective } from 'app/site/base/base-view'; import { @@ -39,6 +41,7 @@ export class PollFormComponent * The form-group for the meta-info. */ public contentForm: FormGroup; + public parentErrorStateMatcher = new ParentErrorStateMatcher(); public PollType = PollType; public PollPropertyVerbose = PollPropertyVerbose; @@ -102,6 +105,10 @@ export class PollFormComponent } } + public get isEVotingSelected(): boolean { + return this.contentForm.get('type').value && this.contentForm.get('type').value !== 'analog'; + } + /** * Constructor. Retrieves necessary metadata from the pollService, * injects the poll itself @@ -141,11 +148,7 @@ export class PollFormComponent } } - Object.keys(this.contentForm.controls).forEach(key => { - if (this.data[key]) { - this.contentForm.get(key).patchValue(this.data[key]); - } - }); + this.patchForm(this.contentForm); } this.updatePollValues(this.contentForm.value); this.updatePercentBases(this.contentForm.get('pollmethod').value); @@ -173,6 +176,25 @@ export class PollFormComponent this.setWarning(); } + /** + * Generic recursive helper function to patch the form + * will transitive move poll.min_votes_amount and poll.max_votes_amount into + * form.votes_amount.min_votes_amount/max_votes_amount + * @param formGroup + */ + private patchForm(formGroup: FormGroup): void { + for (const key of Object.keys(formGroup.controls)) { + const currentControl = formGroup.controls[key]; + if (currentControl instanceof FormControl) { + if (this.data[key]) { + currentControl.patchValue(this.data[key]); + } + } else if (currentControl instanceof FormGroup) { + this.patchForm(currentControl); + } + } + } + private disablePollType(): void { this.contentForm.get('type').disable(); } @@ -243,8 +265,14 @@ export class PollFormComponent } } - public getValues(): Partial { - return { ...this.data, ...this.contentForm.value }; + public getValues(): Partial { + return { ...this.data, ...this.serializeForm(this.contentForm) }; + } + + private serializeForm(formGroup: FormGroup): Partial { + const formData = { ...formGroup.value, ...formGroup.value.votes_amount }; + delete formData.votes_amount; + return formData; } /** @@ -300,8 +328,13 @@ export class PollFormComponent pollmethod: ['', Validators.required], onehundred_percent_base: ['', Validators.required], majority_method: ['', Validators.required], - max_votes_amount: [1, [Validators.required, Validators.min(1)]], - min_votes_amount: [1, [Validators.required, Validators.min(1)]], + votes_amount: this.fb.group( + { + max_votes_amount: [1, [Validators.required, Validators.min(1)]], + min_votes_amount: [1, [Validators.required, Validators.min(1)]] + }, + { validator: isNumberRange('min_votes_amount', 'max_votes_amount') } + ), groups_id: [], global_yes: [false], global_no: [false],