Show latest meaningfull poll results in autopilot
Shows the latest meaningfull poll result in autopilot The last published poll result from the corresponding content object
This commit is contained in:
parent
2943c969ab
commit
10614ca57b
@ -1,5 +1,5 @@
|
||||
<div *ngIf="poll">
|
||||
<table class="assignment-result-table">
|
||||
<table class="assignment-result-table" *ngIf="hasResults && canSeeResults">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="voting-option">{{ 'Candidates' | translate }}</th>
|
||||
@ -51,4 +51,18 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- No results yet -->
|
||||
<div *ngIf="!hasResults">
|
||||
<i>
|
||||
{{ 'No results yet.' | translate }}
|
||||
</i>
|
||||
</div>
|
||||
|
||||
<!-- Has results, but user cannot see -->
|
||||
<div *ngIf="hasResults && !canSeeResults">
|
||||
<i>
|
||||
{{ 'Counting of votes is in progress ...' | translate }}
|
||||
</i>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,12 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { BaseComponent } from 'app/base.component';
|
||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { AssignmentPollMethod } from 'app/shared/models/assignments/assignment-poll';
|
||||
import { PollState } from 'app/shared/models/poll/base-poll';
|
||||
import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll';
|
||||
import { AssignmentPollService } from 'app/site/assignments/modules/assignment-poll/services/assignment-poll.service';
|
||||
import { PollData, PollTableData, VotingResult } from 'app/site/polls/services/poll.service';
|
||||
@ -10,16 +16,18 @@ import { PollData, PollTableData, VotingResult } from 'app/site/polls/services/p
|
||||
templateUrl: './assignment-poll-detail-content.component.html',
|
||||
styleUrls: ['./assignment-poll-detail-content.component.scss']
|
||||
})
|
||||
export class AssignmentPollDetailContentComponent {
|
||||
export class AssignmentPollDetailContentComponent extends BaseComponent {
|
||||
@Input()
|
||||
public poll: ViewAssignmentPoll | PollData;
|
||||
|
||||
public constructor(private pollService: AssignmentPollService) {}
|
||||
|
||||
private get method(): string {
|
||||
return this.poll.pollmethod;
|
||||
}
|
||||
|
||||
private get state(): PollState {
|
||||
return this.poll.state;
|
||||
}
|
||||
|
||||
public get showYHeader(): boolean {
|
||||
return this.isMethodY || this.isMethodYN || this.isMethodYNA;
|
||||
}
|
||||
@ -44,10 +52,35 @@ export class AssignmentPollDetailContentComponent {
|
||||
return this.method === AssignmentPollMethod.YNA;
|
||||
}
|
||||
|
||||
public get isFinished(): boolean {
|
||||
return this.state === PollState.Finished;
|
||||
}
|
||||
|
||||
public get isPublished(): boolean {
|
||||
return this.state === PollState.Published;
|
||||
}
|
||||
|
||||
public get tableData(): PollTableData[] {
|
||||
return this.pollService.generateTableData(this.poll);
|
||||
}
|
||||
|
||||
public get hasResults(): boolean {
|
||||
return this.isFinished || this.isPublished;
|
||||
}
|
||||
|
||||
public get canSeeResults(): boolean {
|
||||
return this.operator.hasPerms(this.permission.assignmentsCanManage) || this.isPublished;
|
||||
}
|
||||
|
||||
public constructor(
|
||||
titleService: Title,
|
||||
translateService: TranslateService,
|
||||
private pollService: AssignmentPollService,
|
||||
private operator: OperatorService
|
||||
) {
|
||||
super(titleService, translateService);
|
||||
}
|
||||
|
||||
public getVoteClass(votingResult: VotingResult): string {
|
||||
const votingClass = votingResult.vote;
|
||||
if (this.isMethodN && votingClass === 'no') {
|
||||
|
@ -74,8 +74,10 @@ export class ChartsComponent extends BaseViewComponentDirective {
|
||||
|
||||
@Input()
|
||||
public set data(inputData: ChartData) {
|
||||
if (inputData) {
|
||||
this.progressInputData(inputData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The options used for the charts.
|
||||
@ -85,6 +87,9 @@ export class ChartsComponent extends BaseViewComponentDirective {
|
||||
return {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation: {
|
||||
duration: 0
|
||||
},
|
||||
tooltips: {
|
||||
enabled: false
|
||||
},
|
||||
@ -96,6 +101,9 @@ export class ChartsComponent extends BaseViewComponentDirective {
|
||||
return {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation: {
|
||||
duration: 0
|
||||
},
|
||||
tooltips: {
|
||||
enabled: false
|
||||
},
|
||||
|
@ -1,4 +1,5 @@
|
||||
<div class="result-wrapper" *ngIf="hasVotes">
|
||||
<div class="result-wrapper" *ngIf="poll">
|
||||
<ng-container *ngIf="hasResults && canSeeResults">
|
||||
<!-- result table -->
|
||||
<table class="result-table">
|
||||
<tbody>
|
||||
@ -6,7 +7,7 @@
|
||||
<th></th>
|
||||
<th colspan="2">{{ 'Votes' | translate }}</th>
|
||||
</tr>
|
||||
<tr *ngFor="let row of getTableData()" [class]="row.votingOption">
|
||||
<tr *ngFor="let row of tableData; trackBy: trackByIndex" [class]="row.votingOption">
|
||||
<!-- YNA/Valid etc -->
|
||||
<td>
|
||||
<os-icon-container *ngIf="row.value[0].icon" [icon]="row.value[0].icon" [size]="iconSize">
|
||||
@ -33,7 +34,22 @@
|
||||
</table>
|
||||
|
||||
<!-- Chart -->
|
||||
<div class="doughnut-chart" *ngIf="showChart">
|
||||
<os-charts type="doughnut" [data]="chartData | async"></os-charts>
|
||||
<div class="doughnut-chart">
|
||||
<os-charts type="doughnut" [data]="chartData"></os-charts>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<!-- No results yet -->
|
||||
<div *ngIf="!hasResults">
|
||||
<i>
|
||||
{{ 'No results yet.' | translate }}
|
||||
</i>
|
||||
</div>
|
||||
|
||||
<!-- Has results, but user cannot see -->
|
||||
<div *ngIf="hasResults && !canSeeResults">
|
||||
<i>
|
||||
{{ 'Counting of votes is in progress ...' | translate }}
|
||||
</i>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,7 +1,11 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { BaseComponent } from 'app/base.component';
|
||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { PollState } from 'app/shared/models/poll/base-poll';
|
||||
import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
||||
import { MotionPollService } from 'app/site/motions/services/motion-poll.service';
|
||||
import { PollData, PollTableData } from 'app/site/polls/services/poll.service';
|
||||
@ -10,31 +14,65 @@ import { ChartData } from '../charts/charts.component';
|
||||
@Component({
|
||||
selector: 'os-motion-poll-detail-content',
|
||||
templateUrl: './motion-poll-detail-content.component.html',
|
||||
styleUrls: ['./motion-poll-detail-content.component.scss']
|
||||
styleUrls: ['./motion-poll-detail-content.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class MotionPollDetailContentComponent implements OnInit {
|
||||
@Input()
|
||||
public poll: ViewMotionPoll | PollData;
|
||||
export class MotionPollDetailContentComponent extends BaseComponent {
|
||||
private _poll: ViewMotionPoll | PollData;
|
||||
|
||||
public chartData: ChartData;
|
||||
public tableData: PollTableData[];
|
||||
|
||||
@Input()
|
||||
public chartData: BehaviorSubject<ChartData>;
|
||||
public set poll(pollData: ViewMotionPoll | PollData) {
|
||||
this._poll = pollData;
|
||||
this.setTableData();
|
||||
this.setChartData();
|
||||
this.cd.markForCheck();
|
||||
}
|
||||
|
||||
public get poll(): ViewMotionPoll | PollData {
|
||||
return this._poll;
|
||||
}
|
||||
|
||||
@Input()
|
||||
public iconSize: 'large' | 'gigantic' = 'large';
|
||||
|
||||
public get hasVotes(): boolean {
|
||||
return this.poll && !!this.poll.options;
|
||||
private get state(): PollState {
|
||||
return this.poll.state;
|
||||
}
|
||||
|
||||
public constructor(private motionPollService: MotionPollService) {}
|
||||
|
||||
public ngOnInit(): void {}
|
||||
|
||||
public getTableData(): PollTableData[] {
|
||||
return this.motionPollService.generateTableData(this.poll);
|
||||
public get hasResults(): boolean {
|
||||
return this.isFinished || this.isPublished;
|
||||
}
|
||||
|
||||
public get showChart(): boolean {
|
||||
return this.motionPollService.showChart(this.poll) && this.chartData && !!this.chartData.value;
|
||||
public get isFinished(): boolean {
|
||||
return this.state === PollState.Finished;
|
||||
}
|
||||
|
||||
public get isPublished(): boolean {
|
||||
return this.state === PollState.Published;
|
||||
}
|
||||
|
||||
public get canSeeResults(): boolean {
|
||||
return this.operator.hasPerms(this.permission.motionsCanManagePolls) || this.isPublished;
|
||||
}
|
||||
|
||||
public constructor(
|
||||
titleService: Title,
|
||||
translate: TranslateService,
|
||||
private pollService: MotionPollService,
|
||||
private cd: ChangeDetectorRef,
|
||||
private operator: OperatorService
|
||||
) {
|
||||
super(titleService, translate);
|
||||
}
|
||||
|
||||
private setTableData(): void {
|
||||
this.tableData = this.pollService.generateTableData(this.poll);
|
||||
}
|
||||
|
||||
private setChartData(): void {
|
||||
this.chartData = this.pollService.generateChartData(this.poll);
|
||||
}
|
||||
}
|
||||
|
@ -28,22 +28,17 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="assignment-result-wrapper" *ngIf="poll && poll.stateHasVotes">
|
||||
<div class="assignment-result-wrapper" *ngIf="poll">
|
||||
<!-- Result Table -->
|
||||
<os-assignment-poll-detail-content [poll]="poll"></os-assignment-poll-detail-content>
|
||||
|
||||
<!-- Result Chart -->
|
||||
<div class="chart-wrapper">
|
||||
<os-charts
|
||||
class="assignment-result-chart"
|
||||
*ngIf="chartDataSubject.value"
|
||||
[labels]="candidatesLabels"
|
||||
[data]="chartDataSubject | async"
|
||||
></os-charts>
|
||||
<div class="chart-wrapper" *ngIf="showResults && poll.stateHasVotes">
|
||||
<os-charts class="assignment-result-chart" [labels]="candidatesLabels" [data]="chartData"></os-charts>
|
||||
</div>
|
||||
|
||||
<!-- Single Votes Table -->
|
||||
<div class="named-result-table" *ngIf="poll.type === 'named'">
|
||||
<div class="named-result-table" *ngIf="showResults && poll.stateHasVotes && poll.type === 'named'">
|
||||
<h3>{{ 'Single votes' | translate }}</h3>
|
||||
<os-list-view-table
|
||||
class="single-votes-table"
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, ViewEncapsulation } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ViewEncapsulation } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
@ -12,6 +12,7 @@ import { AssignmentVoteRepositoryService } from 'app/core/repositories/assignmen
|
||||
import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||
import { ChartData } from 'app/shared/components/charts/charts.component';
|
||||
import { VoteValue } from 'app/shared/models/poll/base-vote';
|
||||
import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll';
|
||||
import { BasePollDetailComponentDirective } from 'app/site/polls/components/base-poll-detail.component';
|
||||
@ -22,6 +23,7 @@ import { AssignmentPollService } from '../../services/assignment-poll.service';
|
||||
selector: 'os-assignment-poll-detail',
|
||||
templateUrl: './assignment-poll-detail.component.html',
|
||||
styleUrls: ['./assignment-poll-detail.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class AssignmentPollDetailComponent extends BasePollDetailComponentDirective<
|
||||
@ -38,6 +40,14 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponentDirect
|
||||
|
||||
public isVoteWeightActive: boolean;
|
||||
|
||||
public get showResults(): boolean {
|
||||
return this.hasPerms() || this.poll.isPublished;
|
||||
}
|
||||
|
||||
public get chartData(): ChartData {
|
||||
return this.pollService.generateChartData(this.poll);
|
||||
}
|
||||
|
||||
public constructor(
|
||||
title: Title,
|
||||
translate: TranslateService,
|
||||
@ -51,7 +61,8 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponentDirect
|
||||
protected pollService: AssignmentPollService,
|
||||
votesRepo: AssignmentVoteRepositoryService,
|
||||
protected operator: OperatorService,
|
||||
private router: Router
|
||||
private router: Router,
|
||||
protected cd: ChangeDetectorRef
|
||||
) {
|
||||
super(
|
||||
title,
|
||||
@ -64,7 +75,8 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponentDirect
|
||||
pollDialog,
|
||||
pollService,
|
||||
votesRepo,
|
||||
operator
|
||||
operator,
|
||||
cd
|
||||
);
|
||||
configService
|
||||
.get<boolean>('users_activate_vote_weight')
|
||||
|
@ -12,9 +12,7 @@
|
||||
<div class="italic spacer-bottom-20">
|
||||
<span *osPerms="'assignments.can_manage'; and: poll.type === 'pseudoanonymous'">
|
||||
<button mat-icon-button color="warn" (click)="openVotingWarning()">
|
||||
<mat-icon>
|
||||
warning
|
||||
</mat-icon>
|
||||
<mat-icon> warning </mat-icon>
|
||||
</button>
|
||||
</span>
|
||||
<span *ngIf="poll.type !== 'analog'"> {{ poll.typeVerbose | translate }} · </span>
|
||||
@ -64,19 +62,8 @@
|
||||
|
||||
<!-- Chart / Table -->
|
||||
<div *ngIf="poll.stateHasVotes" class="poll-result-wrapper">
|
||||
<div *osPerms="'assignments.can_manage'; or: poll.isPublished">
|
||||
<os-assignment-poll-detail-content
|
||||
routerLink="/assignments/polls/{{ poll.id }}"
|
||||
[poll]="poll"
|
||||
></os-assignment-poll-detail-content>
|
||||
</div>
|
||||
|
||||
<!-- Cannot see unpublished -->
|
||||
<div *osPerms="'assignments.can_manage'; complement: true">
|
||||
<span *ngIf="poll.isFinished">
|
||||
{{ 'Counting of votes is in progress ...' | translate }}
|
||||
</span>
|
||||
</div>
|
||||
<os-assignment-poll-detail-content routerLink="/assignments/polls/{{ poll.id }}" [poll]="poll">
|
||||
</os-assignment-poll-detail-content>
|
||||
</div>
|
||||
|
||||
<!-- Poll progress bar -->
|
||||
@ -95,9 +82,7 @@
|
||||
matTooltip="{{ 'More' | translate }}"
|
||||
*ngIf="poll.isPublished"
|
||||
>
|
||||
<mat-icon class="small-icon">
|
||||
visibility
|
||||
</mat-icon>
|
||||
<mat-icon class="small-icon"> visibility </mat-icon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
|
||||
@ -90,7 +90,8 @@ export class CinemaComponent extends BaseViewComponentDirective implements OnIni
|
||||
private projectorService: ProjectorService,
|
||||
private projectorRepo: ProjectorRepositoryService,
|
||||
private closService: CurrentListOfSpeakersService,
|
||||
private listOfSpeakersRepo: ListOfSpeakersRepositoryService
|
||||
private listOfSpeakersRepo: ListOfSpeakersRepositoryService,
|
||||
private cd: ChangeDetectorRef
|
||||
) {
|
||||
super(title, translate, snackBar);
|
||||
}
|
||||
|
@ -1,13 +1,32 @@
|
||||
<mat-card class="os-card" *ngFor="let poll of polls; trackBy: identifyPoll">
|
||||
<ng-container
|
||||
*ngFor="let poll of polls; trackBy: identifyPoll"
|
||||
[ngTemplateOutlet]="pollArea"
|
||||
[ngTemplateOutletContext]="{ poll: poll, last: false }"
|
||||
></ng-container>
|
||||
|
||||
<ng-container
|
||||
*ngIf="lastPublishedPoll && !hasProjectedModelOpenPolls"
|
||||
[ngTemplateOutlet]="pollArea"
|
||||
[ngTemplateOutletContext]="{ poll: lastPublishedPoll, last: true }"
|
||||
></ng-container>
|
||||
|
||||
<ng-template #pollArea let-poll="poll" let-last="last">
|
||||
<mat-card class="os-card">
|
||||
<p class="subtitle-text">
|
||||
<a [routerLink]="getPollDetailLink(poll)" [state]="{ back: 'true' }">{{ getPollVoteTitle(poll) }}</a>
|
||||
</p>
|
||||
|
||||
<div *ngIf="poll.pollClassType === 'motion'">
|
||||
<os-motion-poll-vote [poll]="poll" *ngIf="poll.canBeVotedFor()"></os-motion-poll-vote>
|
||||
<os-motion-poll-vote [poll]="poll" *ngIf="poll.canBeVotedFor() && !last"></os-motion-poll-vote>
|
||||
<os-motion-poll-detail-content [poll]="lastPublishedPoll" *ngIf="last"></os-motion-poll-detail-content>
|
||||
</div>
|
||||
|
||||
<div *ngIf="poll.pollClassType === 'assignment'">
|
||||
<os-assignment-poll-vote [poll]="poll" *ngIf="poll.canBeVotedFor()"></os-assignment-poll-vote>
|
||||
<os-assignment-poll-vote [poll]="poll" *ngIf="poll.canBeVotedFor() && !last"></os-assignment-poll-vote>
|
||||
<os-assignment-poll-detail-content
|
||||
[poll]="lastPublishedPoll"
|
||||
*ngIf="last"
|
||||
></os-assignment-poll-detail-content>
|
||||
</div>
|
||||
</mat-card>
|
||||
</mat-card>
|
||||
</ng-template>
|
||||
|
@ -1,25 +1,52 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { ViewAssignment } from 'app/site/assignments/models/view-assignment';
|
||||
import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll';
|
||||
import { BaseViewComponentDirective } from 'app/site/base/base-view';
|
||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
||||
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';
|
||||
|
||||
@Component({
|
||||
selector: 'os-poll-collection',
|
||||
templateUrl: './poll-collection.component.html',
|
||||
styleUrls: ['./poll-collection.component.scss']
|
||||
styleUrls: ['./poll-collection.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class PollCollectionComponent extends BaseViewComponentDirective implements OnInit {
|
||||
public polls: ViewBasePoll[];
|
||||
|
||||
public lastPublishedPoll: ViewBasePoll;
|
||||
|
||||
private _currentProjection: BaseViewModel<any>;
|
||||
|
||||
public get currentProjection(): BaseViewModel<any> {
|
||||
return this._currentProjection;
|
||||
}
|
||||
|
||||
/**
|
||||
* CLEANUP: This function belongs to "HasViewPolls"/ ViewModelWithPolls
|
||||
*/
|
||||
public get hasProjectedModelOpenPolls(): boolean {
|
||||
if (this.currentProjection instanceof ViewMotion || this.currentProjection instanceof ViewAssignment) {
|
||||
const currPolls: ViewMotionPoll[] | ViewAssignmentPoll[] = this.currentProjection.polls;
|
||||
return currPolls.some((p: ViewMotionPoll | ViewAssignmentPoll) => p.isStarted);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Input()
|
||||
private currentProjection: BaseViewModel<any>;
|
||||
public set currentProjection(viewModel: BaseViewModel<any>) {
|
||||
this._currentProjection = viewModel;
|
||||
this.updateLastPublished();
|
||||
}
|
||||
|
||||
private get showExtendedTitle(): boolean {
|
||||
const areAllPollsSameModel = this.polls.every(
|
||||
@ -27,7 +54,7 @@ export class PollCollectionComponent extends BaseViewComponentDirective implemen
|
||||
);
|
||||
|
||||
if (this.currentProjection && areAllPollsSameModel) {
|
||||
return this.polls[0].getContentObject() !== this.currentProjection;
|
||||
return this.polls[0]?.getContentObject() !== this.currentProjection;
|
||||
} else {
|
||||
return !areAllPollsSameModel;
|
||||
}
|
||||
@ -37,7 +64,8 @@ export class PollCollectionComponent extends BaseViewComponentDirective implemen
|
||||
title: Title,
|
||||
translate: TranslateService,
|
||||
snackBar: MatSnackBar,
|
||||
private pollService: PollListObservableService
|
||||
private pollService: PollListObservableService,
|
||||
private cd: ChangeDetectorRef
|
||||
) {
|
||||
super(title, translate, snackBar);
|
||||
}
|
||||
@ -46,9 +74,17 @@ export class PollCollectionComponent extends BaseViewComponentDirective implemen
|
||||
this.subscriptions.push(
|
||||
this.pollService
|
||||
.getViewModelListObservable()
|
||||
.pipe(map(polls => polls.filter(poll => poll.canBeVotedFor())))
|
||||
.pipe(
|
||||
map(polls => {
|
||||
return polls.filter(poll => {
|
||||
return poll.canBeVotedFor();
|
||||
});
|
||||
})
|
||||
)
|
||||
.subscribe(polls => {
|
||||
this.polls = polls;
|
||||
this.cd.markForCheck();
|
||||
this.updateLastPublished();
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -57,6 +93,10 @@ export class PollCollectionComponent extends BaseViewComponentDirective implemen
|
||||
return poll.id;
|
||||
}
|
||||
|
||||
public getPollDetailLink(poll: ViewBasePoll): string {
|
||||
return poll.parentLink;
|
||||
}
|
||||
|
||||
public getPollVoteTitle(poll: ViewBasePoll): string {
|
||||
const contentObject = poll.getContentObject();
|
||||
const listTitle = contentObject.getListTitle();
|
||||
@ -70,7 +110,35 @@ export class PollCollectionComponent extends BaseViewComponentDirective implemen
|
||||
}
|
||||
}
|
||||
|
||||
public getPollDetailLink(poll: ViewBasePoll): string {
|
||||
return poll.parentLink;
|
||||
/**
|
||||
* Helper function to detect new latest published polls and set them.
|
||||
*/
|
||||
private updateLastPublished(): void {
|
||||
const lastPublished = this.getLastfinshedPoll(this.currentProjection);
|
||||
if (lastPublished !== this.lastPublishedPoll) {
|
||||
this.lastPublishedPoll = lastPublished;
|
||||
this.cd.markForCheck();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CLEANUP: This function belongs to "HasViewPolls"/ ViewModelWithPolls
|
||||
* *class* (is an interface right now)
|
||||
*
|
||||
* @param viewModel
|
||||
*/
|
||||
private getLastfinshedPoll(viewModel: BaseViewModel): ViewBasePoll {
|
||||
if (viewModel instanceof ViewMotion || viewModel instanceof ViewAssignment) {
|
||||
let currPolls: ViewMotionPoll[] | ViewAssignmentPoll[] = viewModel.polls;
|
||||
/**
|
||||
* Although it should, since the union type could use `.filter
|
||||
* without any problem, without an any cast it will not work
|
||||
*/
|
||||
currPolls = (currPolls as any[])
|
||||
.filter((p: ViewMotionPoll | ViewAssignmentPoll) => p.stateHasVotes)
|
||||
.reverse();
|
||||
return currPolls[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||
import { Searchable } from 'app/site/base/searchable';
|
||||
import { SlideOptions } from 'app/site/base/slide-options';
|
||||
import { ViewMediafile } from 'app/site/mediafiles/models/view-mediafile';
|
||||
import { HasViewPolls } from 'app/site/polls/models/has-view-polls';
|
||||
import { ViewTag } from 'app/site/tags/models/view-tag';
|
||||
import { ViewUser } from 'app/site/users/models/view-user';
|
||||
import { AmendmentType } from '../motions.constants';
|
||||
@ -355,7 +356,7 @@ export class ViewMotion
|
||||
}
|
||||
}
|
||||
|
||||
interface TIMotionRelations {
|
||||
interface TIMotionRelations extends HasViewPolls<ViewMotionPoll> {
|
||||
category?: ViewCategory;
|
||||
submitters: ViewSubmitter[];
|
||||
supporters?: ViewUser[];
|
||||
@ -369,7 +370,6 @@ interface TIMotionRelations {
|
||||
amendments?: ViewMotion[];
|
||||
changeRecommendations?: ViewMotionChangeRecommendation[];
|
||||
diffLines?: DiffLinesInParagraph[];
|
||||
polls: ViewMotionPoll[];
|
||||
}
|
||||
|
||||
export interface ViewMotion extends MotionWithoutNestedModels, TIMotionRelations {}
|
||||
|
@ -29,14 +29,10 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p *ngIf="!poll.hasVotes || !poll.stateHasVotes">{{ 'No results yet.' | translate }}</p>
|
||||
|
||||
<div *ngIf="poll.stateHasVotes && (hasPerms() || poll.isPublished)">
|
||||
<os-motion-poll-detail-content [poll]="poll" [chartData]="chartDataSubject">
|
||||
</os-motion-poll-detail-content>
|
||||
<os-motion-poll-detail-content [poll]="poll"></os-motion-poll-detail-content>
|
||||
|
||||
<!-- Named table: only show if votes are present -->
|
||||
<div class="named-result-table" *ngIf="poll.type === 'named'">
|
||||
<div class="named-result-table" *ngIf="showResults && poll.stateHasVotes && poll.type === 'named'">
|
||||
<h2>{{ 'Single votes' | translate }}</h2>
|
||||
<os-list-view-table
|
||||
*ngIf="votesDataObservable"
|
||||
@ -92,7 +88,6 @@
|
||||
{{ 'The individual votes were anonymized.' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="poll-content">
|
||||
<small *ngIf="poll.groups && poll.type && poll.type !== 'analog'">
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ViewEncapsulation } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
@ -44,6 +44,10 @@ export class MotionPollDetailComponent extends BasePollDetailComponentDirective<
|
||||
|
||||
public isVoteWeightActive: boolean;
|
||||
|
||||
public get showResults(): boolean {
|
||||
return this.hasPerms() || this.poll.isPublished;
|
||||
}
|
||||
|
||||
public constructor(
|
||||
title: Title,
|
||||
translate: TranslateService,
|
||||
@ -57,7 +61,8 @@ export class MotionPollDetailComponent extends BasePollDetailComponentDirective<
|
||||
votesRepo: MotionVoteRepositoryService,
|
||||
configService: ConfigService,
|
||||
protected operator: OperatorService,
|
||||
private router: Router
|
||||
private router: Router,
|
||||
protected cd: ChangeDetectorRef
|
||||
) {
|
||||
super(
|
||||
title,
|
||||
@ -70,7 +75,8 @@ export class MotionPollDetailComponent extends BasePollDetailComponentDirective<
|
||||
pollDialog,
|
||||
pollService,
|
||||
votesRepo,
|
||||
operator
|
||||
operator,
|
||||
cd
|
||||
);
|
||||
configService
|
||||
.get<boolean>('users_activate_vote_weight')
|
||||
|
@ -23,9 +23,7 @@
|
||||
<div class="italic spacer-bottom-20">
|
||||
<span *osPerms="'motions.can_manage_polls'; and: poll.type === 'pseudoanonymous'">
|
||||
<button mat-icon-button color="warn" (click)="openVotingWarning()">
|
||||
<mat-icon>
|
||||
warning
|
||||
</mat-icon>
|
||||
<mat-icon>warning</mat-icon>
|
||||
</button>
|
||||
</span>
|
||||
|
||||
@ -42,7 +40,12 @@
|
||||
|
||||
<!-- Change state button -->
|
||||
<div *osPerms="'motions.can_manage_polls'; and: !hideChangeState">
|
||||
<button mat-stroked-button [ngClass]="pollStateActions[poll.state].css" (click)="changeState(poll.nextState)" [disabled]="stateChangePending">
|
||||
<button
|
||||
mat-stroked-button
|
||||
[ngClass]="pollStateActions[poll.state].css"
|
||||
(click)="changeState(poll.nextState)"
|
||||
[disabled]="stateChangePending"
|
||||
>
|
||||
<mat-icon> {{ pollStateActions[poll.state].icon }}</mat-icon>
|
||||
<span class="next-state-label">
|
||||
<ng-container *ngIf="!stateChangePending">
|
||||
@ -66,9 +69,7 @@
|
||||
<!-- Detail link -->
|
||||
<div class="poll-detail-button-wrapper">
|
||||
<a mat-icon-button [routerLink]="pollLink" matTooltip="{{ 'More' | translate }}" *ngIf="poll.isPublished">
|
||||
<mat-icon class="small-icon">
|
||||
visibility
|
||||
</mat-icon>
|
||||
<mat-icon class="small-icon">visibility</mat-icon>
|
||||
</a>
|
||||
</div>
|
||||
</mat-card>
|
||||
@ -82,34 +83,7 @@
|
||||
</ng-template>
|
||||
|
||||
<ng-template #viewTemplate>
|
||||
<!-- Result Chart and legend -->
|
||||
<div class="poll-chart-wrapper" *osPerms="'motions.can_manage_polls'; or: poll.isPublished">
|
||||
<div class="vote-legend" [routerLink]="pollLink">
|
||||
|
||||
<!-- any (click)-binding in an *ngFor-loop of dynamic length will break the view on iOS.
|
||||
Therefore, we have to provide the trackBy-function here.
|
||||
I suppose there would be debug output, but i-devices are not giving any.
|
||||
The error-handling-service (PR #5131) might be helpful here. Nothing easy to find. -->
|
||||
<div *ngFor="let row of reducedPollTableData; trackBy: trackByIndex" [class]="row.votingOption">
|
||||
<os-icon-container [icon]="row.value[0].icon" size="large">
|
||||
{{ row.value[0].amount | parsePollNumber }}
|
||||
<span *ngIf="row.value[0].showPercent">
|
||||
{{ row.value[0].amount | pollPercentBase: poll:'motion' }}
|
||||
</span>
|
||||
</os-icon-container>
|
||||
</div>
|
||||
</div>
|
||||
<div class="doughnut-chart" [routerLink]="pollLink">
|
||||
<os-charts *ngIf="showChart" type="doughnut" [data]="chartDataSubject | async"></os-charts>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- In Progress hint -->
|
||||
<div class="motion-couting-in-progress-hint" *osPerms="'motions.can_manage_polls'; complement: true">
|
||||
<span *ngIf="poll.isFinished">
|
||||
{{ 'Counting of votes is in progress ...' | translate }}
|
||||
</span>
|
||||
</div>
|
||||
<os-motion-poll-detail-content [poll]="poll" [routerLink]="pollLink"></os-motion-poll-detail-content>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #emptyTemplate>
|
||||
|
@ -15,7 +15,6 @@ import { MotionPollDialogService } from 'app/site/motions/services/motion-poll-d
|
||||
import { MotionPollPdfService } from 'app/site/motions/services/motion-poll-pdf.service';
|
||||
import { MotionPollService } from 'app/site/motions/services/motion-poll.service';
|
||||
import { BasePollComponent } from 'app/site/polls/components/base-poll.component';
|
||||
import { PollTableData } from 'app/site/polls/services/poll.service';
|
||||
|
||||
/**
|
||||
* Component to show a motion-poll.
|
||||
@ -29,8 +28,6 @@ export class MotionPollComponent extends BasePollComponent<ViewMotionPoll, Motio
|
||||
@Input()
|
||||
public set poll(value: ViewMotionPoll) {
|
||||
this.initPoll(value);
|
||||
const chartData = this.pollService.generateChartData(value);
|
||||
this.chartDataSubject.next(chartData);
|
||||
}
|
||||
|
||||
public get poll(): ViewMotionPoll {
|
||||
@ -41,16 +38,6 @@ export class MotionPollComponent extends BasePollComponent<ViewMotionPoll, Motio
|
||||
return `/motions/polls/${this.poll.id}`;
|
||||
}
|
||||
|
||||
public get showChart(): boolean {
|
||||
return this.pollService.showChart(this.poll);
|
||||
}
|
||||
|
||||
public get reducedPollTableData(): PollTableData[] {
|
||||
return this.pollService
|
||||
.generateTableData(this.poll)
|
||||
.filter(data => ['yes', 'no', 'abstain', 'votesinvalid'].includes(data.votingOption));
|
||||
}
|
||||
|
||||
public get showPoll(): boolean {
|
||||
if (this.poll) {
|
||||
if (
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Directive, OnInit } from '@angular/core';
|
||||
import { ChangeDetectorRef, Directive, OnInit } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
@ -70,11 +70,6 @@ export abstract class BasePollDetailComponentDirective<V extends ViewBasePoll, S
|
||||
*/
|
||||
public labels: Label[] = [];
|
||||
|
||||
/**
|
||||
* Subject, that holds the data for the chart.
|
||||
*/
|
||||
public chartDataSubject: BehaviorSubject<ChartData> = new BehaviorSubject(null);
|
||||
|
||||
// The observable for the votes-per-user table
|
||||
public votesDataObservable: Observable<BaseVoteData[]>;
|
||||
|
||||
@ -106,7 +101,8 @@ export abstract class BasePollDetailComponentDirective<V extends ViewBasePoll, S
|
||||
protected pollDialog: BasePollDialogService<V, S>,
|
||||
protected pollService: S,
|
||||
protected votesRepo: BaseRepository<ViewBaseVote, BaseVote, object>,
|
||||
protected operator: OperatorService
|
||||
protected operator: OperatorService,
|
||||
protected cd: ChangeDetectorRef
|
||||
) {
|
||||
super(title, translate, matSnackbar);
|
||||
this.setup();
|
||||
@ -187,14 +183,6 @@ export abstract class BasePollDetailComponentDirective<V extends ViewBasePoll, S
|
||||
*/
|
||||
protected abstract createVotesData(): void;
|
||||
|
||||
/**
|
||||
* Initializes data for the shown chart.
|
||||
* Could be overwritten to implement custom chart data.
|
||||
*/
|
||||
protected initChartData(): void {
|
||||
this.chartDataSubject.next(this.pollService.generateChartData(this.poll));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper-function to search for this poll and display data or create a new one.
|
||||
*/
|
||||
@ -206,8 +194,8 @@ export abstract class BasePollDetailComponentDirective<V extends ViewBasePoll, S
|
||||
if (poll) {
|
||||
this.poll = poll;
|
||||
this.createVotesData();
|
||||
this.initChartData();
|
||||
this.optionsLoaded.resolve();
|
||||
this.cd.markForCheck();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ViewBasePoll } from './view-base-poll';
|
||||
|
||||
export interface HasViewPolls<T extends ViewBasePoll> {
|
||||
polls: T[];
|
||||
polls?: T[];
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
MajorityMethod,
|
||||
PercentBase,
|
||||
PollColor,
|
||||
PollState,
|
||||
PollType,
|
||||
VOTE_UNDOCUMENTED
|
||||
} from 'app/shared/models/poll/base-poll';
|
||||
@ -104,6 +105,7 @@ export const PollMajorityMethod: CalculableMajorityMethod[] = [
|
||||
export interface PollData {
|
||||
pollmethod: string;
|
||||
type: string;
|
||||
state: PollState;
|
||||
onehundred_percent_base: string;
|
||||
options: PollDataOption[];
|
||||
votesvalid: number;
|
||||
|
@ -7,6 +7,6 @@
|
||||
<h2 class="poll-title">{{ data.data.poll.title }}</h2>
|
||||
</div>
|
||||
<div *ngIf="data.data.poll.state === PollState.Published">
|
||||
<os-motion-poll-detail-content [poll]="pollData" [chartData]="chartDataSubject" iconSize="gigantic"></os-motion-poll-detail-content>
|
||||
<os-motion-poll-detail-content [poll]="pollData" iconSize="gigantic"></os-motion-poll-detail-content>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
Loading…
Reference in New Issue
Block a user