diff --git a/client/src/app/core/translate/marked-translations.ts b/client/src/app/core/translate/marked-translations.ts
index 66b1808ae..0f035365c 100644
--- a/client/src/app/core/translate/marked-translations.ts
+++ b/client/src/app/core/translate/marked-translations.ts
@@ -173,6 +173,7 @@ _('Number of all participants');
_('Use the following custom number');
_('Custom number of ballot papers');
_('Voting');
+_('Click here to vote');
// subgroup PDF export
_('PDF export');
_('Title for PDF documents of motions');
diff --git a/client/src/app/core/ui-services/banner.service.ts b/client/src/app/core/ui-services/banner.service.ts
index d91e325a4..7bc1a6619 100644
--- a/client/src/app/core/ui-services/banner.service.ts
+++ b/client/src/app/core/ui-services/banner.service.ts
@@ -7,8 +7,7 @@ export interface BannerDefinition {
class?: string;
icon?: string;
text?: string;
- bgColor?: string;
- color?: string;
+ subText?: string;
link?: string;
largerOnMobileView?: boolean;
}
diff --git a/client/src/app/core/ui-services/voting-banner.service.ts b/client/src/app/core/ui-services/voting-banner.service.ts
index 6c72e7c7c..6d24cfe66 100644
--- a/client/src/app/core/ui-services/voting-banner.service.ts
+++ b/client/src/app/core/ui-services/voting-banner.service.ts
@@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
+import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll';
import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
import { ViewBasePoll } from 'app/site/polls/models/view-base-poll';
import { PollListObservableService } from 'app/site/polls/services/poll-list-observable.service';
@@ -15,6 +16,8 @@ import { VotingService } from './voting.service';
export class VotingBannerService {
private currentBanner: BannerDefinition;
+ private subText = 'Click here to vote';
+
public constructor(
pollListObservableService: PollListObservableService,
private banner: BannerService,
@@ -58,6 +61,7 @@ export class VotingBannerService {
private createBanner(text: string, link: string): BannerDefinition {
return {
text: text,
+ subText: this.subText,
link: link,
icon: 'how_to_vote',
largerOnMobileView: true
@@ -72,11 +76,13 @@ export class VotingBannerService {
* @returns The title.
*/
private getTextForPoll(poll: ViewBasePoll): string {
- return poll instanceof ViewMotionPoll
- ? `${this.translate.instant('Motion') + ' ' + poll.motion.getIdentifierOrTitle()}: ${this.translate.instant(
- 'Voting is open'
- )}`
- : `${poll.getTitle()}: ${this.translate.instant('Ballot is open')}`;
+ if (poll instanceof ViewMotionPoll) {
+ return `${this.translate.instant('Motion')} ${poll.motion.getIdentifierOrTitle()}: ${this.translate.instant(
+ 'Voting opened'
+ )}`;
+ } else if (poll instanceof ViewAssignmentPoll) {
+ return `${poll.assignment.getTitle()}: ${this.translate.instant('Ballot openened!')}`;
+ }
}
/**
diff --git a/client/src/app/shared/components/banner/banner.component.html b/client/src/app/shared/components/banner/banner.component.html
index df3bb7d76..a3bd4b20e 100644
--- a/client/src/app/shared/components/banner/banner.component.html
+++ b/client/src/app/shared/components/banner/banner.component.html
@@ -16,6 +16,9 @@
{{ banner.icon }} {{ banner.text }}
+
+ {{ banner.subText | translate }}
+
diff --git a/client/src/app/shared/components/banner/banner.component.scss b/client/src/app/shared/components/banner/banner.component.scss
index 9e0aa2ceb..539b3ce2c 100644
--- a/client/src/app/shared/components/banner/banner.component.scss
+++ b/client/src/app/shared/components/banner/banner.component.scss
@@ -3,12 +3,12 @@
.banner {
&.larger-on-mobile {
@include set-breakpoint-lower(sm) {
- height: 40px;
+ min-height: 40px;
}
}
position: relative; // was fixed before to prevent the overflow
- height: 20px;
+ min-height: 20px;
line-height: 20px;
width: 100%;
text-align: center;
@@ -18,7 +18,6 @@
border-bottom: 1px solid white;
a {
- display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
diff --git a/client/src/app/shared/components/banner/banner.component.scss-theme.scss b/client/src/app/shared/components/banner/banner.component.scss-theme.scss
index 0eef81f1c..20866ad83 100644
--- a/client/src/app/shared/components/banner/banner.component.scss-theme.scss
+++ b/client/src/app/shared/components/banner/banner.component.scss-theme.scss
@@ -2,10 +2,10 @@
/** Custom component theme. Only lives in a specific scope */
@mixin os-banner-style($theme) {
- $primary: map-get($theme, primary);
+ $accent: map-get($theme, accent);
/** style for the offline-banner */
.banner {
- background: mat-color($primary, 900);
+ background: mat-color($accent, 500);
}
}
diff --git a/client/src/app/shared/components/charts/charts.component.ts b/client/src/app/shared/components/charts/charts.component.ts
index 70b7fe482..0c24b665e 100644
--- a/client/src/app/shared/components/charts/charts.component.ts
+++ b/client/src/app/shared/components/charts/charts.component.ts
@@ -199,14 +199,24 @@ export class ChartsComponent extends BaseViewComponent {
labels: {}
},
scales: {
- xAxes: [{ ticks: { beginAtZero: true, stepSize: 1 } }],
- yAxes: [{ ticks: { beginAtZero: true } }]
- },
- plugins: {
- datalabels: {
- anchor: 'end',
- align: 'end'
- }
+ xAxes: [
+ {
+ gridLines: {
+ drawOnChartArea: false
+ },
+ ticks: { beginAtZero: true, stepSize: 1 },
+ stacked: true
+ }
+ ],
+ yAxes: [
+ {
+ gridLines: {
+ drawOnChartArea: false
+ },
+ ticks: { beginAtZero: true, mirror: true, labelOffset: -20 },
+ stacked: true
+ }
+ ]
}
};
@@ -251,16 +261,6 @@ export class ChartsComponent extends BaseViewComponent {
super(title, translate, matSnackbar);
}
- /**
- * Changes the chart-options, if the `stackedBar` is used.
- */
- private setupStackedBar(): void {
- this.chartOptions.scales = Object.assign(this.chartOptions.scales, {
- xAxes: [{ stacked: true }],
- yAxes: [{ stacked: true }]
- });
- }
-
private setupBar(): void {
if (!this.chartData.every(date => date.barThickness && date.maxBarThickness)) {
this.chartData = this.chartData.map(chartDate => ({
@@ -284,7 +284,6 @@ export class ChartsComponent extends BaseViewComponent {
fontSize: 14,
boxWidth: 40
};
- break;
}
this.cd.detectChanges();
}
@@ -292,7 +291,6 @@ export class ChartsComponent extends BaseViewComponent {
private checkChartType(chartType?: ChartType): void {
let type = chartType || this._type;
if (type === 'stackedBar') {
- this.setupStackedBar();
this.setupBar();
type = 'horizontalBar';
}
diff --git a/client/src/app/shared/components/check-input/check-input.component.ts b/client/src/app/shared/components/check-input/check-input.component.ts
index 0b4a29356..22292dd3a 100644
--- a/client/src/app/shared/components/check-input/check-input.component.ts
+++ b/client/src/app/shared/components/check-input/check-input.component.ts
@@ -88,12 +88,10 @@ export class CheckInputComponent extends BaseViewComponent implements OnInit, Co
* @param obj the value from the parent form. Type "any" is required by the interface
*/
public writeValue(obj: string | number): void {
- if (obj) {
- if (obj === this.checkboxValue) {
- this.checkboxStateChanged(true);
- } else {
- this.contentForm.patchValue(obj);
- }
+ if (obj && obj === this.checkboxValue) {
+ this.checkboxStateChanged(true);
+ } else {
+ this.contentForm.patchValue(obj);
}
}
diff --git a/client/src/app/shared/components/voting-privacy-warning/voting-privacy-warning.component.html b/client/src/app/shared/components/voting-privacy-warning/voting-privacy-warning.component.html
new file mode 100644
index 000000000..d28ec9c38
--- /dev/null
+++ b/client/src/app/shared/components/voting-privacy-warning/voting-privacy-warning.component.html
@@ -0,0 +1,18 @@
+
+ Online voting is impossible to secure
+
+
+
+
+ During voting, OpenSlides does not store the individual user ID of the voter. This in no way means that a
+ non-nominal vote is completely anonymous and secure. You cannot track the decisions of your voters after the
+ data has been submitted. The validity of the data cannot always be guaranteed, especially if you use OpenSlides
+ in a distributed online setup. You are responsible for your own actions.
+
+
+
+
+
+ I know the risk
+
+
diff --git a/client/src/app/shared/components/voting-privacy-warning/voting-privacy-warning.component.scss b/client/src/app/shared/components/voting-privacy-warning/voting-privacy-warning.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/client/src/app/shared/components/voting-privacy-warning/voting-privacy-warning.component.spec.ts b/client/src/app/shared/components/voting-privacy-warning/voting-privacy-warning.component.spec.ts
new file mode 100644
index 000000000..9190d7963
--- /dev/null
+++ b/client/src/app/shared/components/voting-privacy-warning/voting-privacy-warning.component.spec.ts
@@ -0,0 +1,26 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { E2EImportsModule } from 'e2e-imports.module';
+
+import { VotingPrivacyWarningComponent } from './voting-privacy-warning.component';
+
+describe('VotingPrivacyWarningComponent', () => {
+ let component: VotingPrivacyWarningComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [E2EImportsModule]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(VotingPrivacyWarningComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/client/src/app/shared/components/voting-privacy-warning/voting-privacy-warning.component.ts b/client/src/app/shared/components/voting-privacy-warning/voting-privacy-warning.component.ts
new file mode 100644
index 000000000..da99280e7
--- /dev/null
+++ b/client/src/app/shared/components/voting-privacy-warning/voting-privacy-warning.component.ts
@@ -0,0 +1,12 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'os-voting-privacy-warning',
+ templateUrl: './voting-privacy-warning.component.html',
+ styleUrls: ['./voting-privacy-warning.component.scss']
+})
+export class VotingPrivacyWarningComponent implements OnInit {
+ public constructor() {}
+
+ public ngOnInit(): void {}
+}
diff --git a/client/src/app/shared/models/poll/base-poll.ts b/client/src/app/shared/models/poll/base-poll.ts
index a78f1eb04..95166aada 100644
--- a/client/src/app/shared/models/poll/base-poll.ts
+++ b/client/src/app/shared/models/poll/base-poll.ts
@@ -76,6 +76,10 @@ export abstract class BasePoll<
return this.onehundred_percent_base === PercentBase.Valid || this.onehundred_percent_base === PercentBase.Cast;
}
+ public get isPercentBaseCast(): boolean {
+ return this.onehundred_percent_base === PercentBase.Cast;
+ }
+
/**
* Determine if the state is finished or published
*/
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts
index 3c7209208..94fb404ff 100644
--- a/client/src/app/shared/shared.module.ts
+++ b/client/src/app/shared/shared.module.ts
@@ -114,7 +114,6 @@ import { LocalizedDatePipe } from './pipes/localized-date.pipe';
import { ChartsComponent } from './components/charts/charts.component';
import { CheckInputComponent } from './components/check-input/check-input.component';
import { BannerComponent } from './components/banner/banner.component';
-import { BasePollDialogComponent } from 'app/site/polls/components/base-poll-dialog.component';
import { PollFormComponent } from 'app/site/polls/components/poll-form/poll-form.component';
import { MotionPollDialogComponent } from 'app/site/motions/modules/motion-poll/motion-poll-dialog/motion-poll-dialog.component';
import { AssignmentPollDialogComponent } from 'app/site/assignments/components/assignment-poll-dialog/assignment-poll-dialog.component';
@@ -122,6 +121,7 @@ import { ParsePollNumberPipe } from './pipes/parse-poll-number.pipe';
import { ReversePipe } from './pipes/reverse.pipe';
import { PollKeyVerbosePipe } from './pipes/poll-key-verbose.pipe';
import { PollPercentBasePipe } from './pipes/poll-percent-base.pipe';
+import { VotingPrivacyWarningComponent } from './components/voting-privacy-warning/voting-privacy-warning.component';
/**
* Share Module for all "dumb" components and pipes.
@@ -285,7 +285,8 @@ import { PollPercentBasePipe } from './pipes/poll-percent-base.pipe';
ParsePollNumberPipe,
ReversePipe,
PollKeyVerbosePipe,
- PollPercentBasePipe
+ PollPercentBasePipe,
+ VotingPrivacyWarningComponent
],
declarations: [
PermsDirective,
@@ -342,7 +343,8 @@ import { PollPercentBasePipe } from './pipes/poll-percent-base.pipe';
ParsePollNumberPipe,
ReversePipe,
PollKeyVerbosePipe,
- PollPercentBasePipe
+ PollPercentBasePipe,
+ VotingPrivacyWarningComponent
],
providers: [
{
@@ -373,7 +375,8 @@ import { PollPercentBasePipe } from './pipes/poll-percent-base.pipe';
ProgressSnackBarComponent,
SuperSearchComponent,
MotionPollDialogComponent,
- AssignmentPollDialogComponent
+ AssignmentPollDialogComponent,
+ VotingPrivacyWarningComponent
]
})
export class SharedModule {}
diff --git a/client/src/app/site/assignments/components/assignment-detail/assignment-detail.component.ts b/client/src/app/site/assignments/components/assignment-detail/assignment-detail.component.ts
index 6ad6054f4..87d8bdd5a 100644
--- a/client/src/app/site/assignments/components/assignment-detail/assignment-detail.component.ts
+++ b/client/src/app/site/assignments/components/assignment-detail/assignment-detail.component.ts
@@ -313,7 +313,7 @@ export class AssignmentDetailComponent extends BaseViewComponent implements OnIn
collectionString: ViewAssignmentPoll.COLLECTIONSTRING,
assignment_id: this.assignment.id,
assignment: this.assignment,
- ...this.assignmentPollService.getDefaultPollData()
+ ...this.assignmentPollService.getDefaultPollData(this.assignment.id)
};
this.pollDialog.openDialog(dialogData);
diff --git a/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.html b/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.html
index 61674131d..b5a93ec8f 100644
--- a/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.html
+++ b/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.html
@@ -20,14 +20,14 @@
{{ poll.title }}
{{ poll.typeVerbose | translate }}
-
-
-
+
+
+
- Candidates
-
+ Candidates
+
Yes
@@ -35,42 +35,50 @@
Votes
- No
- Abstain
+ No
+ Abstain
-
-
- {{ row.votingOption | pollKeyVerbose | translate }}
-
-
-
- {{ row.votingOptionSubtitle }}
-
-
-
-
+
+
+
+ {{ row.votingOption | pollKeyVerbose | translate }}
+
+
+
+ {{ row.votingOptionSubtitle }}
+
+
+
+
+
- {{ vote.amount | parsePollNumber }}
{{ vote.amount | pollPercentBase: poll }}
+ {{ vote.amount | parsePollNumber }}
+
-
+
+
@@ -112,7 +120,7 @@
-
+
-
-
diff --git a/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.scss b/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.scss
index 94725e90f..1dc97d084 100644
--- a/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.scss
+++ b/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.scss
@@ -2,24 +2,22 @@
@import '~assets/styles/poll-colors.scss';
.assignment-result-wrapper {
- margin-top: 2em;
- display: grid;
- grid-gap: 10px;
-
.assignment-result-table {
+ margin-top: 2em;
+ display: block;
+ overflow-x: auto;
border-collapse: collapse;
th {
- text-align: left;
font-weight: initial;
}
tr {
height: 48px;
- }
- tr:last-child {
- border-bottom: none;
+ td:first-child {
+ padding-right: 1em;
+ }
}
tr.sums {
@@ -30,39 +28,57 @@
}
}
+ .result {
+ text-align: right;
+ padding-left: 1em;
+ }
+
+ .voting-option {
+ min-width: 200px;
+ width: 100%;
+ text-align: left;
+ }
+
.user + .sums {
td {
- padding-top: 4em;
+ padding-top: 2em;
}
}
+
+ .single-result {
+ white-space: pre;
+ }
}
- .pie-chart {
- margin-left: auto;
- margin-right: auto;
- width: 50%;
- }
-}
-
-.single-vote-result + .single-vote-result {
- margin-top: 1em;
-}
-
-.named-result-table {
- .mat-form-field {
- font-size: 14px;
- width: 100%;
+ .chart-wrapper {
+ margin-top: 2em;
+ .pie-chart {
+ margin-left: auto;
+ margin-right: auto;
+ width: 50%;
+ }
}
- .single-votes-table {
- display: block;
- height: 500px;
- }
+ .named-result-table {
+ .mat-form-field {
+ font-size: 14px;
+ width: 100%;
+ }
- .vote-field {
- text-align: center;
- width: 100%;
- padding-right: 12px;
+ .single-votes-table {
+ display: block;
+ height: 500px;
+
+ .single-vote-result + .single-vote-result {
+ margin-top: 1em;
+ }
+ }
+
+ .vote-field {
+ text-align: center;
+ width: 100%;
+ padding-right: 12px;
+ }
}
}
diff --git a/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.ts b/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.ts
index e59b4ee59..6420ae119 100644
--- a/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.ts
+++ b/client/src/app/site/assignments/components/assignment-poll-detail/assignment-poll-detail.component.ts
@@ -34,11 +34,9 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent {
+ let component: AssignmentPollDialogComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [E2EImportsModule],
+ providers: [
+ { provide: MatDialogRef, useValue: {} },
+ {
+ provide: MAT_DIALOG_DATA,
+ useValue: {}
+ }
+ ]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(AssignmentPollDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/client/src/app/site/assignments/components/assignment-poll-dialog/assignment-poll-dialog.component.ts b/client/src/app/site/assignments/components/assignment-poll-dialog/assignment-poll-dialog.component.ts
index 9acb1b84b..5004194d0 100644
--- a/client/src/app/site/assignments/components/assignment-poll-dialog/assignment-poll-dialog.component.ts
+++ b/client/src/app/site/assignments/components/assignment-poll-dialog/assignment-poll-dialog.component.ts
@@ -85,15 +85,23 @@ export class AssignmentPollDialogComponent extends BasePollDialogComponent ({
- user_id: user.id,
- user: user
- }),
- {}
- );
+ if (this.pollData) {
+ if (this.pollData.options) {
+ this.options = this.pollData.options;
+ } else if (
+ this.pollData.assignment &&
+ this.pollData.assignment.candidates &&
+ this.pollData.assignment.candidates.length
+ ) {
+ this.options = this.pollData.assignment.candidates.map(
+ user => ({
+ user_id: user.id,
+ user: user
+ }),
+ {}
+ );
+ }
+ }
this.subscriptions.push(
this.pollForm.contentForm.get('pollmethod').valueChanges.subscribe(() => {
diff --git a/client/src/app/site/assignments/components/assignment-poll-vote/assignment-poll-vote.component.html b/client/src/app/site/assignments/components/assignment-poll-vote/assignment-poll-vote.component.html
index 2cc0d3069..711534cfe 100644
--- a/client/src/app/site/assignments/components/assignment-poll-vote/assignment-poll-vote.component.html
+++ b/client/src/app/site/assignments/components/assignment-poll-vote/assignment-poll-vote.component.html
@@ -1,5 +1,4 @@
-
Your vote is valid!
@@ -22,7 +21,12 @@
}"
>
-
{{ option.user.getFullName() }}
+
+ {{ option.user.short_name }}
+
+ {{ option.user.getLevelAndNumber() }}
+
+
{{ 'Unknown user' | translate }}
diff --git a/client/src/app/site/assignments/components/assignment-poll-vote/assignment-poll-vote.component.ts b/client/src/app/site/assignments/components/assignment-poll-vote/assignment-poll-vote.component.ts
index 8f4dce333..ef99f8f13 100644
--- a/client/src/app/site/assignments/components/assignment-poll-vote/assignment-poll-vote.component.ts
+++ b/client/src/app/site/assignments/components/assignment-poll-vote/assignment-poll-vote.component.ts
@@ -122,12 +122,17 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponent {
- if ((n === optionId && vote === 'Y') !== (this.currentVotes[n] === 'Yes')) {
- o[n] = 1; // TODO: allow multiple votes per candidate
- } else {
- o[n] = 0;
+ o[n] = 0;
+ if (this.poll.votes_amount === 1) {
+ if (n === optionId && this.currentVotes[n] !== 'Yes') {
+ o[n] = 1;
+ }
+ } else if ((n === optionId) !== (this.currentVotes[n] === 'Yes')) {
+ o[n] = 1;
}
+
return o;
}, {});
} else {
@@ -135,6 +140,7 @@ export class AssignmentPollVoteComponent extends BasePollVoteComponent
+
@@ -21,7 +21,7 @@
@@ -45,14 +45,25 @@
-
-
+
+
+
+
+
+
+
+
+
+ {{ 'Counting is in progress' | translate }}
+
+
diff --git a/client/src/app/site/assignments/components/assignment-poll/assignment-poll.component.scss b/client/src/app/site/assignments/components/assignment-poll/assignment-poll.component.scss
index 3f8ef3fbb..47fa85aa5 100644
--- a/client/src/app/site/assignments/components/assignment-poll/assignment-poll.component.scss
+++ b/client/src/app/site/assignments/components/assignment-poll/assignment-poll.component.scss
@@ -29,11 +29,4 @@
.publish-poll-button {
color: $poll-publish-color;
}
-
- .doughnut-chart {
- display: block;
- max-width: 300px;
- margin-left: auto;
- margin-right: auto;
- }
}
diff --git a/client/src/app/site/assignments/components/assignment-poll/assignment-poll.component.ts b/client/src/app/site/assignments/components/assignment-poll/assignment-poll.component.ts
index 9dd1fd9cb..151eafe5a 100644
--- a/client/src/app/site/assignments/components/assignment-poll/assignment-poll.component.ts
+++ b/client/src/app/site/assignments/components/assignment-poll/assignment-poll.component.ts
@@ -6,7 +6,6 @@ import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
-import { OperatorService } from 'app/core/core-services/operator.service';
import { AssignmentPollRepositoryService } from 'app/core/repositories/assignments/assignment-poll-repository.service';
import { PromptService } from 'app/core/ui-services/prompt.service';
import { ChartType } from 'app/shared/components/charts/charts.component';
@@ -39,7 +38,7 @@ export class AssignmentPollComponent extends BasePollComponent
({
- votingOption: key.vote,
- class: 'sums',
- value: [
- {
- amount: this[key.vote],
- hide: key.hide,
- showPercent: key.showPercent
- } as VotingResult
- ]
- }))
+ ...this.sumTableKeys
+ .filter(key => {
+ return !key.hide;
+ })
+ .map(key => ({
+ votingOption: key.vote,
+ class: 'sums',
+ value: [
+ {
+ amount: this[key.vote],
+ hide: key.hide,
+ showPercent: key.showPercent
+ } as VotingResult
+ ]
+ }))
);
return tableData;
}
diff --git a/client/src/app/site/assignments/services/assignment-poll.service.ts b/client/src/app/site/assignments/services/assignment-poll.service.ts
index 4dbbc79ec..f428d6647 100644
--- a/client/src/app/site/assignments/services/assignment-poll.service.ts
+++ b/client/src/app/site/assignments/services/assignment-poll.service.ts
@@ -54,14 +54,21 @@ export class AssignmentPollService extends PollService {
.subscribe(method => (this.defaultPollMethod = method));
}
- public getDefaultPollData(): AssignmentPoll {
- const poll = new AssignmentPoll(super.getDefaultPollData());
- const length = this.pollRepo.getViewModelList().filter(item => item.assignment_id === poll.assignment_id)
- .length;
+ public getDefaultPollData(contextId?: number): AssignmentPoll {
+ const poll = new AssignmentPoll({
+ ...super.getDefaultPollData()
+ });
- poll.title = !length ? this.translate.instant('Ballot') : `${this.translate.instant('Ballot')} (${length + 1})`;
+ poll.title = this.translate.instant('Ballot');
poll.pollmethod = this.defaultPollMethod;
+ if (contextId) {
+ const length = this.pollRepo.getViewModelList().filter(item => item.assignment_id === contextId).length;
+ if (length) {
+ poll.title += ` (${length + 1})`;
+ }
+ }
+
return poll;
}
diff --git a/client/src/app/site/motions/models/view-motion-poll.ts b/client/src/app/site/motions/models/view-motion-poll.ts
index 9d4cc0ce7..957ec7d31 100644
--- a/client/src/app/site/motions/models/view-motion-poll.ts
+++ b/client/src/app/site/motions/models/view-motion-poll.ts
@@ -34,6 +34,10 @@ export class ViewMotionPoll extends ViewBasePoll
-
+
{{ motion.star ? 'star' : 'star_border' }}
@@ -460,12 +465,16 @@
-
-
- poll
- New vote
-
-
+
+ add
+ New vote
+
diff --git a/client/src/app/site/motions/modules/motion-detail/components/motion-detail/motion-detail.component.scss b/client/src/app/site/motions/modules/motion-detail/components/motion-detail/motion-detail.component.scss
index 535e88b18..87e61c8ad 100644
--- a/client/src/app/site/motions/modules/motion-detail/components/motion-detail/motion-detail.component.scss
+++ b/client/src/app/site/motions/modules/motion-detail/components/motion-detail/motion-detail.component.scss
@@ -5,12 +5,7 @@ span {
}
.create-poll-button {
- margin-top: 10px;
- padding: 0px !important;
- button {
- display: block;
- width: 100%;
- }
+ margin-bottom: 1em;
}
.extra-controls-slot {
diff --git a/client/src/app/site/motions/modules/motion-detail/components/motion-detail/motion-detail.component.ts b/client/src/app/site/motions/modules/motion-detail/components/motion-detail/motion-detail.component.ts
index b7941b02b..66cec02e8 100644
--- a/client/src/app/site/motions/modules/motion-detail/components/motion-detail/motion-detail.component.ts
+++ b/client/src/app/site/motions/modules/motion-detail/components/motion-detail/motion-detail.component.ts
@@ -1633,7 +1633,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
collectionString: ViewMotionPoll.COLLECTIONSTRING,
motion_id: this.motion.id,
motion: this.motion,
- ...this.motionPollService.getDefaultPollData()
+ ...this.motionPollService.getDefaultPollData(this.motion.id)
};
this.pollDialog.openDialog(dialogData);
diff --git a/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.html b/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.html
index 0a12465e2..9fababa6a 100644
--- a/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.html
+++ b/client/src/app/site/motions/modules/motion-poll/motion-poll-detail/motion-poll-detail.component.html
@@ -67,9 +67,8 @@
-
+
{
+export class MotionPollDialogComponent extends BasePollDialogComponent implements OnInit {
public PercentBaseVerbose = PercentBaseVerbose;
@ViewChild('pollForm', { static: false })
@@ -30,6 +30,9 @@ export class MotionPollDialogComponent extends BasePollDialogComponent
) {
super(title, translate, matSnackbar, dialogRef);
+ }
+
+ public ngOnInit(): void {
this.createDialog();
}
@@ -54,12 +57,12 @@ export class MotionPollDialogComponent extends BasePollDialogComponent {
+ if (confirmed) {
+ this.pollRepo.vote(vote, this.poll.id).catch(this.raiseError);
+ }
+ });
+ } else {
+ this.pollRepo.vote(vote, this.poll.id).catch(this.raiseError);
+ }
}
}
diff --git a/client/src/app/site/motions/modules/motion-poll/motion-poll/motion-poll.component.html b/client/src/app/site/motions/modules/motion-poll/motion-poll/motion-poll.component.html
index 25ca5369a..6edc09b8f 100644
--- a/client/src/app/site/motions/modules/motion-poll/motion-poll/motion-poll.component.html
+++ b/client/src/app/site/motions/modules/motion-poll/motion-poll/motion-poll.component.html
@@ -6,11 +6,19 @@
- {{ poll.title }}
+ {{ poll.title | translate }}
+
+
+
+ warning
+
+
+
+
{{ poll.typeVerbose | translate }} ·
@@ -18,7 +26,7 @@
- {{ poll.stateVerbose }}
+ {{ poll.stateVerbose | translate }}
@@ -32,13 +40,8 @@
-
-
+
+
{{ pollStateActions[poll.state].icon }}
{{ poll.nextStateActionVerbose | translate }}
@@ -73,22 +76,12 @@
-
-
- {{ voteYes | parsePollNumber }}
- {{ voteYes | pollPercentBase: poll }}
-
-
-
-
- {{ voteNo | parsePollNumber }}
- {{ voteNo | pollPercentBase: poll }}
-
-
-
-
- {{ voteAbstain | parsePollNumber }}
- {{ voteAbstain | pollPercentBase: poll }}
+
+
+ {{ row.value[0].amount | parsePollNumber }}
+
+ {{ row.value[0].amount | pollPercentBase: poll }}
+
@@ -113,7 +106,7 @@
-
+
{{ 'Edit to enter votes.' | translate }}
diff --git a/client/src/app/site/motions/modules/motion-poll/motion-poll/motion-poll.component.scss b/client/src/app/site/motions/modules/motion-poll/motion-poll/motion-poll.component.scss
index 643feb3db..152d0e8e2 100644
--- a/client/src/app/site/motions/modules/motion-poll/motion-poll/motion-poll.component.scss
+++ b/client/src/app/site/motions/modules/motion-poll/motion-poll/motion-poll.component.scss
@@ -47,15 +47,15 @@
margin-top: 20px;
}
- .votes-yes {
+ .yes {
color: $votes-yes-color;
}
- .votes-no {
+ .no {
color: $votes-no-color;
}
- .votes-abstain {
+ .abstain {
color: $votes-abstain-color;
}
}
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 6faceabaf..da0bfd06d 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
@@ -6,10 +6,14 @@ import { TranslateService } from '@ngx-translate/core';
import { MotionPollRepositoryService } from 'app/core/repositories/motions/motion-poll-repository.service';
import { PromptService } from 'app/core/ui-services/prompt.service';
+import { VotingPrivacyWarningComponent } from 'app/shared/components/voting-privacy-warning/voting-privacy-warning.component';
+import { PollType } from 'app/shared/models/poll/base-poll';
+import { infoDialogSettings } from 'app/shared/utils/dialog-settings';
import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
import { MotionPollDialogService } from 'app/site/motions/services/motion-poll-dialog.service';
import { MotionPollPdfService } from 'app/site/motions/services/motion-poll-pdf.service';
import { BasePollComponent } from 'app/site/polls/components/base-poll.component';
+import { PollTableData } from 'app/site/polls/models/view-base-poll';
import { PollService } from 'app/site/polls/services/poll.service';
/**
@@ -28,19 +32,7 @@ export class MotionPollComponent extends BasePollComponent
{
@Input()
public set poll(value: ViewMotionPoll) {
this.initPoll(value);
-
const chartData = this.pollService.generateChartData(value);
- for (const data of chartData) {
- if (data.label === 'YES') {
- this.voteYes = data.data[0];
- }
- if (data.label === 'NO') {
- this.voteNo = data.data[0];
- }
- if (data.label === 'ABSTAIN') {
- this.voteAbstain = data.data[0];
- }
- }
this.chartDataSubject.next(chartData);
}
@@ -52,48 +44,17 @@ export class MotionPollComponent extends BasePollComponent {
return `/motions/polls/${this.poll.id}`;
}
- /**
- * Number of votes for `Yes`.
- */
- public set voteYes(n: number) {
- this._voteYes = n;
- }
-
- public get voteYes(): number {
- return this._voteYes;
- }
-
- /**
- * Number of votes for `No`.
- */
- public set voteNo(n: number) {
- this._voteNo = n;
- }
-
- public get voteNo(): number {
- return this._voteNo;
- }
-
- /**
- * Number of votes for `Abstain`.
- */
- public set voteAbstain(n: number) {
- this._voteAbstain = n;
- }
-
- public get voteAbstain(): number {
- return this._voteAbstain;
- }
-
public get showChart(): boolean {
- return this._voteYes >= 0 && this._voteNo >= 0;
+ return this.poll.hasPresentableValues;
}
- private _voteNo: number;
+ public get hideChangeState(): boolean {
+ return this.poll.isPublished || (this.poll.isCreated && this.poll.type === PollType.Analog);
+ }
- private _voteYes: number;
-
- private _voteAbstain: number;
+ public get reducedPollTableData(): PollTableData[] {
+ return this.poll.tableData.filter(data => ['yes', 'no', 'abstain', 'votesinvalid'].includes(data.votingOption));
+ }
/**
* Constructor.
@@ -118,6 +79,10 @@ export class MotionPollComponent extends BasePollComponent {
super(titleService, matSnackBar, translate, dialog, promptService, pollRepo, pollDialog);
}
+ public openVotingWarning(): void {
+ this.dialog.open(VotingPrivacyWarningComponent, infoDialogSettings);
+ }
+
public downloadPdf(): void {
this.pdfService.printBallots(this.poll);
}
diff --git a/client/src/app/site/motions/modules/motion-workflow/components/workflow-detail/workflow-detail.component.ts b/client/src/app/site/motions/modules/motion-workflow/components/workflow-detail/workflow-detail.component.ts
index 7a490dea5..5ef0902fd 100644
--- a/client/src/app/site/motions/modules/motion-workflow/components/workflow-detail/workflow-detail.component.ts
+++ b/client/src/app/site/motions/modules/motion-workflow/components/workflow-detail/workflow-detail.component.ts
@@ -146,7 +146,7 @@ export class WorkflowDetailComponent extends BaseViewComponent implements OnInit
* @param title Set the page title
* @param translate Handle translations
* @param matSnackBar Showing error
- * @param promtService Promts
+ * @param promptService Promts
* @param dialog Opening dialogs
* @param workflowRepo The repository for workflows
* @param route Read out URL paramters
@@ -155,7 +155,7 @@ export class WorkflowDetailComponent extends BaseViewComponent implements OnInit
title: Title,
protected translate: TranslateService, // protected required for ng-translate-extract
matSnackBar: MatSnackBar,
- private promtService: PromptService,
+ private promptService: PromptService,
private dialog: MatDialog,
private workflowRepo: WorkflowRepositoryService,
private stateRepo: StateRepositoryService,
@@ -208,7 +208,7 @@ export class WorkflowDetailComponent extends BaseViewComponent implements OnInit
} else if (result.action === 'delete') {
const content = this.translate.instant('Delete') + ` ${state.name}?`;
- this.promtService.open('Are you sure', content).then(promptResult => {
+ this.promptService.open('Are you sure', content).then(promptResult => {
if (promptResult) {
this.stateRepo.delete(state).then(() => {}, this.raiseError);
}
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 f6b97fd57..db79666a3 100644
--- a/client/src/app/site/motions/services/motion-poll.service.ts
+++ b/client/src/app/site/motions/services/motion-poll.service.ts
@@ -55,13 +55,19 @@ export class MotionPollService extends PollService {
config.get(MotionPoll.defaultGroupsConfig).subscribe(ids => (this.defaultGroupIds = ids));
}
- public getDefaultPollData(): MotionPoll {
+ public getDefaultPollData(contextId?: number): MotionPoll {
const poll = new MotionPoll(super.getDefaultPollData());
- 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.title = this.translate.instant('Vote');
poll.pollmethod = MotionPollMethod.YNA;
+ if (contextId) {
+ const length = this.pollRepo.getViewModelList().filter(item => item.motion_id === contextId).length;
+ if (length) {
+ poll.title += ` (${length + 1})`;
+ }
+ }
+
return poll;
}
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 cc7e4f3ee..ad2fe207e 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
@@ -1,3 +1,4 @@
+import { OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { MatSnackBar } from '@angular/material';
import { MatDialogRef } from '@angular/material/dialog';
@@ -13,7 +14,7 @@ import { ViewBasePoll } from '../models/view-base-poll';
/**
* A dialog for updating the values of a poll.
*/
-export abstract class BasePollDialogComponent extends BaseViewComponent {
+export abstract class BasePollDialogComponent extends BaseViewComponent implements OnInit {
public publishImmediately: boolean;
protected pollForm: PollFormComponent;
@@ -29,6 +30,21 @@ export abstract class BasePollDialogComponent extends Ba
super(title, translate, matSnackbar);
}
+ public ngOnInit(): void {
+ if (this.dialogRef) {
+ // Jasmin/Karma fails here. TODO:
+ this.dialogRef.keydownEvents().subscribe((event: KeyboardEvent) => {
+ if (event.key === 'Enter' && event.shiftKey) {
+ this.submitPoll();
+ }
+
+ if (event.key === 'Escape') {
+ this.dialogRef.close();
+ }
+ });
+ }
+ }
+
/**
* Submits the values from dialog.
*/
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 10df33ba7..4ee04c7eb 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
@@ -9,7 +9,7 @@
- 1" class="poll-preview-meta-info">
+