+
@@ -62,11 +67,7 @@
-
diff --git a/client/src/app/site/motions/modules/motion-poll/motion-poll/motion-poll.component.ts b/client/src/app/site/motions/modules/motion-poll/motion-poll/motion-poll.component.ts
index 7f65317dd..383c5c2f6 100644
--- a/client/src/app/site/motions/modules/motion-poll/motion-poll/motion-poll.component.ts
+++ b/client/src/app/site/motions/modules/motion-poll/motion-poll/motion-poll.component.ts
@@ -1,10 +1,12 @@
import { Component, Input } from '@angular/core';
import { MatDialog, MatSnackBar } from '@angular/material';
import { Title } from '@angular/platform-browser';
+import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject } from 'rxjs';
+import { OperatorService } from 'app/core/core-services/operator.service';
import { MotionPollRepositoryService } from 'app/core/repositories/motions/motion-poll-repository.service';
import { PromptService } from 'app/core/ui-services/prompt.service';
import { ChartData } from 'app/shared/components/charts/charts.component';
@@ -28,7 +30,7 @@ export class MotionPollComponent extends BasePollComponent {
*/
@Input()
public set poll(value: ViewMotionPoll) {
- this._poll = value;
+ this.initPoll(value);
const chartData = this.poll.generateChartData();
for (const data of chartData) {
@@ -54,17 +56,33 @@ export class MotionPollComponent extends BasePollComponent {
/**
* Number of votes for `Yes`.
*/
- public voteYes = 0;
+ // public voteYes = 0;
+ public set voteYes(n: number | string) {
+ this._voteYes = n;
+ }
+
+ public get voteYes(): number | string {
+ return this.verboseForNumber(this._voteYes as number);
+ }
/**
* Number of votes for `No`.
*/
- public voteNo = 0;
+ public set voteNo(n: number | string) {
+ this._voteNo = n;
+ }
- /**
- * The motion-poll.
- */
- private _poll: ViewMotionPoll;
+ public get voteNo(): number | string {
+ return this.verboseForNumber(this._voteNo as number);
+ }
+
+ public get showChart(): boolean {
+ return this._voteYes >= 0 && this._voteNo >= 0;
+ }
+
+ private _voteNo: number | string = 0;
+
+ private _voteYes: number | string = 0;
/**
* Constructor.
@@ -81,10 +99,30 @@ export class MotionPollComponent extends BasePollComponent {
translate: TranslateService,
dialog: MatDialog,
promptService: PromptService,
- public repo: MotionPollRepositoryService,
+ public pollRepo: MotionPollRepositoryService,
pollDialog: MotionPollDialogService,
- public pollService: PollService
+ public pollService: PollService,
+ private router: Router,
+ private operator: OperatorService
) {
- super(titleService, matSnackBar, translate, dialog, promptService, repo, pollDialog);
+ super(titleService, matSnackBar, translate, dialog, promptService, pollRepo, pollDialog);
+ }
+
+ public openPoll(): void {
+ if (this.operator.hasPerms('motions.can_manage_polls')) {
+ this.router.navigate(['motions', 'polls', this.poll.id]);
+ }
+ }
+
+ private verboseForNumber(input: number): number | string {
+ input = Math.trunc(input);
+ switch (input) {
+ case -1:
+ return 'Majority';
+ case -2:
+ return 'Not documented';
+ default:
+ return input;
+ }
}
}
diff --git a/client/src/app/site/motions/services/motion-poll.service.ts b/client/src/app/site/motions/services/motion-poll.service.ts
index fb5f19dd9..895dee8df 100644
--- a/client/src/app/site/motions/services/motion-poll.service.ts
+++ b/client/src/app/site/motions/services/motion-poll.service.ts
@@ -52,7 +52,7 @@ export class MotionPollService extends PollService {
const length = this.pollRepo.getViewModelList().filter(item => item.motion_id === poll.motion_id).length;
poll.title = !length ? this.translate.instant('Vote') : `${this.translate.instant('Vote')} (${length + 1})`;
- poll.pollmethod = MotionPollMethods.YN;
+ poll.pollmethod = MotionPollMethods.YNA;
poll.motion_id = poll.motion_id;
}
}
diff --git a/client/src/app/site/polls/components/base-poll-detail.component.ts b/client/src/app/site/polls/components/base-poll-detail.component.ts
index 1972118f5..e71c2d794 100644
--- a/client/src/app/site/polls/components/base-poll-detail.component.ts
+++ b/client/src/app/site/polls/components/base-poll-detail.component.ts
@@ -11,14 +11,14 @@ import { GroupRepositoryService } from 'app/core/repositories/users/group-reposi
import { BasePollDialogService } from 'app/core/ui-services/base-poll-dialog.service';
import { PromptService } from 'app/core/ui-services/prompt.service';
import { Breadcrumb } from 'app/shared/components/breadcrumb/breadcrumb.component';
-import { ChartData } from 'app/shared/components/charts/charts.component';
-import { PollState } from 'app/shared/models/poll/base-poll';
+import { ChartData, ChartType } from 'app/shared/components/charts/charts.component';
+import { PollState, PollType } from 'app/shared/models/poll/base-poll';
import { BaseViewComponent } from 'app/site/base/base-view';
import { ViewGroup } from 'app/site/users/models/view-group';
import { BasePollRepositoryService } from '../services/base-poll-repository.service';
import { ViewBasePoll } from '../models/view-base-poll';
-export class BasePollDetailComponent extends BaseViewComponent implements OnInit {
+export abstract class BasePollDetailComponent extends BaseViewComponent implements OnInit {
/**
* All the groups of users.
*/
@@ -42,7 +42,8 @@ export class BasePollDetailComponent extends BaseViewCom
/**
* Sets the type of the shown chart, if votes are entered.
*/
- public chartType = 'horizontalBar';
+ // public chartType = 'horizontalBar';
+ public abstract get chartType(): ChartType;
/**
* The different labels for the votes (used for chart).
@@ -99,7 +100,7 @@ export class BasePollDetailComponent extends BaseViewCom
const text = 'Do you really want to delete the selected poll?';
if (await this.promptDialog.open(title, text)) {
- await this.repo.delete(this.poll);
+ this.repo.delete(this.poll).then(() => this.onDeleted());
}
}
@@ -121,12 +122,23 @@ export class BasePollDetailComponent extends BaseViewCom
this.chartDataSubject.next(this.poll.generateChartData());
}
+ protected onDeleted(): void {}
+
+ /**
+ * Called after the poll has been loaded. Meant to be overwritten by subclasses who need initial access to the poll
+ */
+ protected onPollLoaded(): void {}
+
+ protected onStateChanged(): void {}
+
+ protected abstract hasPerms(): boolean;
+
/**
* This checks, if the poll has votes.
*/
private checkData(): void {
if (this.poll.state === 3 || this.poll.state === 4) {
- // this.chartDataSubject.next(this.poll.generateChartData());
+ setTimeout(() => this.chartDataSubject.next(this.poll.generateChartData()));
}
}
@@ -142,7 +154,6 @@ export class BasePollDetailComponent extends BaseViewCom
this.poll = poll;
this.updateBreadcrumbs();
this.checkData();
- this.labels = this.createChartLabels();
this.onPollLoaded();
}
})
@@ -157,23 +168,18 @@ export class BasePollDetailComponent extends BaseViewCom
this.pollDialog.openDialog(this.poll);
}
- /**
- * Called after the poll has been loaded. Meant to be overwritten by subclasses who need initial access to the poll
- */
- public onPollLoaded(): void {}
-
/**
* Action for the different breadcrumbs.
*/
private async changeState(): Promise {
- this.actionWrapper(this.repo.changePollState(this.poll));
+ this.actionWrapper(this.repo.changePollState(this.poll), this.onStateChanged);
}
/**
* Resets the state of a motion-poll.
*/
private async resetState(): Promise {
- this.actionWrapper(this.repo.resetPoll(this.poll));
+ this.actionWrapper(this.repo.resetPoll(this.poll), this.onStateChanged);
}
/**
@@ -183,17 +189,15 @@ export class BasePollDetailComponent extends BaseViewCom
*
* @returns Any promise-like.
*/
- private actionWrapper(action: Promise): any {
- action.then(() => this.checkData()).catch(this.raiseError);
- }
-
- /**
- * Function to create the labels for the chart.
- *
- * @returns An array of `Label`.
- */
- private createChartLabels(): Label[] {
- return ['Number of votes'];
+ private actionWrapper(action: Promise, callback?: () => any): any {
+ action
+ .then(() => {
+ this.checkData();
+ if (callback) {
+ callback();
+ }
+ })
+ .catch(this.raiseError);
}
/**
@@ -220,11 +224,14 @@ export class BasePollDetailComponent extends BaseViewCom
if (!this.poll) {
return null;
}
+ if (!this.hasPerms()) {
+ return null;
+ }
switch (this.poll.state) {
case PollState.Created:
return state === 2 ? () => this.changeState() : null;
case PollState.Started:
- return null;
+ return this.poll.type !== PollType.Analog && state === 3 ? () => this.changeState() : null;
case PollState.Finished:
if (state === 1) {
return () => this.resetState();
diff --git a/client/src/app/site/polls/components/base-poll.component.ts b/client/src/app/site/polls/components/base-poll.component.ts
index 533fda44a..bb98620a6 100644
--- a/client/src/app/site/polls/components/base-poll.component.ts
+++ b/client/src/app/site/polls/components/base-poll.component.ts
@@ -1,23 +1,28 @@
-import { Input } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
+import { BehaviorSubject } from 'rxjs';
import { BasePollDialogService } from 'app/core/ui-services/base-poll-dialog.service';
import { PromptService } from 'app/core/ui-services/prompt.service';
+import { ChartData } from 'app/shared/components/charts/charts.component';
import { PollState } from 'app/shared/models/poll/base-poll';
import { BaseViewComponent } from 'app/site/base/base-view';
import { BasePollRepositoryService } from '../services/base-poll-repository.service';
import { ViewBasePoll } from '../models/view-base-poll';
-export class BasePollComponent extends BaseViewComponent {
- /**
- * The poll represented in this component
- */
- @Input()
- public poll: V;
+export abstract class BasePollComponent extends BaseViewComponent {
+ // /**
+ // * The poll represented in this component
+ // */
+ // @Input()
+ // public abstract set poll(model: V);
+
+ public chartDataSubject: BehaviorSubject = new BehaviorSubject([]);
+
+ protected _poll: V;
public constructor(
titleService: Title,
@@ -25,14 +30,14 @@ export class BasePollComponent extends BaseViewComponent
public translate: TranslateService,
public dialog: MatDialog,
protected promptService: PromptService,
- public repo: BasePollRepositoryService,
+ protected repo: BasePollRepositoryService,
protected pollDialog: BasePollDialogService
) {
super(titleService, translate, matSnackBar);
}
public changeState(key: PollState): void {
- key === PollState.Created ? this.repo.resetPoll(this.poll) : this.repo.changePollState(this.poll);
+ key === PollState.Created ? this.repo.resetPoll(this._poll) : this.repo.changePollState(this._poll);
}
/**
@@ -41,7 +46,7 @@ export class BasePollComponent extends BaseViewComponent
public async onDeletePoll(): Promise {
const title = this.translate.instant('Are you sure you want to delete this poll?');
if (await this.promptService.open(title)) {
- await this.repo.delete(this.poll).catch(this.raiseError);
+ await this.repo.delete(this._poll).catch(this.raiseError);
}
}
@@ -49,6 +54,13 @@ export class BasePollComponent extends BaseViewComponent
* Edits the poll
*/
public openDialog(): void {
- this.pollDialog.openDialog(this.poll);
+ this.pollDialog.openDialog(this._poll);
+ }
+
+ /**
+ * Forces to initialize the poll.
+ */
+ protected initPoll(model: V): void {
+ this._poll = model;
}
}
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 b57d11d21..53667e448 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
@@ -47,13 +47,9 @@
-
+
-
+
{{ option.value | translate }}
@@ -68,4 +64,3 @@
-
\ No newline at end of file
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 5c7cb5de5..d975844da 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
@@ -63,6 +63,12 @@ export class PollFormComponent extends BaseViewComponent implements OnInit {
*/
public pollValues: [string, unknown][] = [];
+ /**
+ * Model for the checkbox.
+ * If true, the given poll will immediately be published.
+ */
+ public publishImmediately = true;
+
/**
* Constructor. Retrieves necessary metadata from the pollService,
* injects the poll itself
@@ -73,18 +79,10 @@ export class PollFormComponent extends BaseViewComponent implements OnInit {
snackbar: MatSnackBar,
private fb: FormBuilder,
private groupRepo: GroupRepositoryService,
- private pollService: PollService
+ public pollService: PollService
) {
super(title, translate, snackbar);
-
- this.contentForm = this.fb.group({
- title: ['', Validators.required],
- type: ['', Validators.required],
- pollmethod: ['', Validators.required],
- onehundred_percent_base: ['', Validators.required],
- majority_method: ['', Validators.required],
- groups_id: [[]]
- });
+ this.initContentForm();
}
/**
@@ -133,6 +131,10 @@ export class PollFormComponent extends BaseViewComponent implements OnInit {
return { ...this.data, ...this.contentForm.value };
}
+ public isValidPercentBaseWithMethod(base: PercentBase): boolean {
+ return !(base === PercentBase.YNA && this.contentForm.get('pollmethod').value === 'YN');
+ }
+
/**
* This updates the poll-values to get correct data in the view.
*
@@ -152,4 +154,15 @@ export class PollFormComponent extends BaseViewComponent implements OnInit {
]);
}
}
+
+ private initContentForm(): void {
+ this.contentForm = this.fb.group({
+ title: ['', Validators.required],
+ type: ['', Validators.required],
+ pollmethod: ['', Validators.required],
+ onehundred_percent_base: ['', Validators.required],
+ majority_method: ['', Validators.required],
+ groups_id: [[]]
+ });
+ }
}
diff --git a/client/src/app/site/polls/models/view-base-poll.ts b/client/src/app/site/polls/models/view-base-poll.ts
index 6e3623e03..ec10da991 100644
--- a/client/src/app/site/polls/models/view-base-poll.ts
+++ b/client/src/app/site/polls/models/view-base-poll.ts
@@ -53,6 +53,15 @@ export const PercentBaseVerbose = {
};
export abstract class ViewBasePoll
= any> extends BaseProjectableViewModel {
+ private _tableData: {}[] = [];
+
+ public get tableData(): {}[] {
+ if (!this._tableData.length) {
+ this._tableData = this.generateTableData();
+ }
+ return this._tableData;
+ }
+
public get poll(): M {
return this._model;
}
@@ -101,7 +110,14 @@ export abstract class ViewBasePoll = any> extends Bas
public abstract getSlide(): ProjectorElementBuildDeskriptor;
+ /**
+ * Initializes labels for a chart.
+ */
+ public abstract initChartLabels(): string[];
+
public abstract generateChartData(): ChartData;
+
+ public abstract generateTableData(): {}[];
}
export interface ViewBasePoll = any> extends BasePoll {
diff --git a/client/src/styles.scss b/client/src/styles.scss
index 8cff8d348..d221bde56 100644
--- a/client/src/styles.scss
+++ b/client/src/styles.scss
@@ -27,6 +27,7 @@
@import './app/site/config/components/config-field/config-field.component.scss-theme.scss';
@import './app/site/motions/modules/motion-detail/components/amendment-create-wizard/amendment-create-wizard.components.scss-theme.scss';
@import './app/site/motions/modules/motion-detail/components/motion-detail-diff/motion-detail-diff.component.scss-theme.scss';
+@import './app/shared/components/banner/banner.component.scss-theme.scss';
/** fonts */
@import './assets/styles/fonts.scss';
@@ -54,6 +55,7 @@ $narrow-spacing: (
@include os-config-field-style($theme);
@include os-amendment-create-wizard-style($theme);
@include os-motion-detail-diff-style($theme);
+ @include os-banner-style($theme);
}
/** Load projector specific SCSS values */