Motion poll detail als slide
Refactor the code to use the motion poll detail als slide component
This commit is contained in:
parent
3c36441967
commit
29a9a09bc6
@ -0,0 +1,39 @@
|
|||||||
|
<div class="result-wrapper" *ngIf="hasVotes">
|
||||||
|
<!-- result table -->
|
||||||
|
<table class="result-table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th colspan="2" translate>Votes</th>
|
||||||
|
</tr>
|
||||||
|
<tr *ngFor="let row of getTableData()" [class]="row.votingOption">
|
||||||
|
<!-- YNA/Valid etc -->
|
||||||
|
<td>
|
||||||
|
<os-icon-container *ngIf="row.value[0].icon" [icon]="row.value[0].icon">
|
||||||
|
{{ row.votingOption | pollKeyVerbose | translate }}
|
||||||
|
</os-icon-container>
|
||||||
|
<span *ngIf="!row.value[0].icon">
|
||||||
|
{{ row.votingOption | pollKeyVerbose | translate }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Percent numbers -->
|
||||||
|
<td class="result-cell-definition">
|
||||||
|
<span *ngIf="row.value[0].showPercent">
|
||||||
|
{{ row.value[0].amount | pollPercentBase: poll }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Voices -->
|
||||||
|
<td class="result-cell-definition">
|
||||||
|
{{ row.value[0].amount | parsePollNumber }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- Chart -->
|
||||||
|
<div class="doughnut-chart" *ngIf="showChart">
|
||||||
|
<os-charts type="doughnut" [data]="chartData" [showLegend]="false" [hasPadding]="false"></os-charts>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,43 @@
|
|||||||
|
@import '~assets/styles/poll-colors.scss';
|
||||||
|
|
||||||
|
.result-wrapper {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 2em;
|
||||||
|
margin: 2em;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
|
||||||
|
.result-table {
|
||||||
|
// display: block;
|
||||||
|
th {
|
||||||
|
text-align: right;
|
||||||
|
font-weight: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
height: 48px;
|
||||||
|
border-bottom: none !important;
|
||||||
|
|
||||||
|
.result-cell-definition {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.yes {
|
||||||
|
color: $votes-yes-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no {
|
||||||
|
color: $votes-no-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.abstain {
|
||||||
|
color: $votes-abstain-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.doughnut-chart {
|
||||||
|
display: block;
|
||||||
|
margin-top: auto;
|
||||||
|
margin-bottom: auto;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { E2EImportsModule } from 'e2e-imports.module';
|
||||||
|
|
||||||
|
import { MotionPollDetailContentComponent } from './motion-poll-detail-content.component';
|
||||||
|
|
||||||
|
describe('MotionPollDetailContentComponent', () => {
|
||||||
|
let component: MotionPollDetailContentComponent;
|
||||||
|
let fixture: ComponentFixture<MotionPollDetailContentComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [E2EImportsModule]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(MotionPollDetailContentComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,37 @@
|
|||||||
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
|
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';
|
||||||
|
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']
|
||||||
|
})
|
||||||
|
export class MotionPollDetailContentComponent implements OnInit {
|
||||||
|
@Input()
|
||||||
|
public poll: ViewMotionPoll | PollData;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
public chartData: BehaviorSubject<ChartData>;
|
||||||
|
|
||||||
|
public get hasVotes(): boolean {
|
||||||
|
return this.poll && !!this.poll.options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor(private motionPollService: MotionPollService) {}
|
||||||
|
|
||||||
|
public ngOnInit(): void {}
|
||||||
|
|
||||||
|
public getTableData(): PollTableData[] {
|
||||||
|
return this.motionPollService.generateTableData(this.poll);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get showChart(): boolean {
|
||||||
|
return this.motionPollService.showChart(this.poll) && this.chartData && !!this.chartData.value;
|
||||||
|
}
|
||||||
|
}
|
@ -73,10 +73,6 @@ export abstract class BasePoll<
|
|||||||
return this.state === PollState.Published;
|
return this.state === PollState.Published;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get isPercentBaseValidOrCast(): boolean {
|
|
||||||
return this.onehundred_percent_base === PercentBase.Valid || this.onehundred_percent_base === PercentBase.Cast;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get isPercentBaseCast(): boolean {
|
public get isPercentBaseCast(): boolean {
|
||||||
return this.onehundred_percent_base === PercentBase.Cast;
|
return this.onehundred_percent_base === PercentBase.Cast;
|
||||||
}
|
}
|
||||||
|
@ -122,6 +122,7 @@ import { ReversePipe } from './pipes/reverse.pipe';
|
|||||||
import { PollKeyVerbosePipe } from './pipes/poll-key-verbose.pipe';
|
import { PollKeyVerbosePipe } from './pipes/poll-key-verbose.pipe';
|
||||||
import { PollPercentBasePipe } from './pipes/poll-percent-base.pipe';
|
import { PollPercentBasePipe } from './pipes/poll-percent-base.pipe';
|
||||||
import { VotingPrivacyWarningComponent } from './components/voting-privacy-warning/voting-privacy-warning.component';
|
import { VotingPrivacyWarningComponent } from './components/voting-privacy-warning/voting-privacy-warning.component';
|
||||||
|
import { MotionPollDetailContentComponent } from './components/motion-poll-detail-content/motion-poll-detail-content.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Share Module for all "dumb" components and pipes.
|
* Share Module for all "dumb" components and pipes.
|
||||||
@ -286,7 +287,8 @@ import { VotingPrivacyWarningComponent } from './components/voting-privacy-warni
|
|||||||
ReversePipe,
|
ReversePipe,
|
||||||
PollKeyVerbosePipe,
|
PollKeyVerbosePipe,
|
||||||
PollPercentBasePipe,
|
PollPercentBasePipe,
|
||||||
VotingPrivacyWarningComponent
|
VotingPrivacyWarningComponent,
|
||||||
|
MotionPollDetailContentComponent
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
PermsDirective,
|
PermsDirective,
|
||||||
@ -344,7 +346,8 @@ import { VotingPrivacyWarningComponent } from './components/voting-privacy-warni
|
|||||||
ReversePipe,
|
ReversePipe,
|
||||||
PollKeyVerbosePipe,
|
PollKeyVerbosePipe,
|
||||||
PollPercentBasePipe,
|
PollPercentBasePipe,
|
||||||
VotingPrivacyWarningComponent
|
VotingPrivacyWarningComponent,
|
||||||
|
MotionPollDetailContentComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
<th class="result voted-no" translate *ngIf="!poll.isMethodY">No</th>
|
<th class="result voted-no" translate *ngIf="!poll.isMethodY">No</th>
|
||||||
<th class="result voted-abstain" translate *ngIf="poll.isMethodYNA">Abstain</th>
|
<th class="result voted-abstain" translate *ngIf="poll.isMethodYNA">Abstain</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr *ngFor="let row of poll.tableData" [class]="row.class">
|
<tr *ngFor="let row of getTableData()" [class]="row.class">
|
||||||
<td class="voting-option">
|
<td class="voting-option">
|
||||||
<div>
|
<div>
|
||||||
<span>
|
<span>
|
||||||
|
@ -13,9 +13,9 @@ import { GroupRepositoryService } from 'app/core/repositories/users/group-reposi
|
|||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { ChartType } from 'app/shared/components/charts/charts.component';
|
import { ChartType } from 'app/shared/components/charts/charts.component';
|
||||||
import { BasePollDetailComponent } from 'app/site/polls/components/base-poll-detail.component';
|
import { BasePollDetailComponent } from 'app/site/polls/components/base-poll-detail.component';
|
||||||
import { VotingResult } from 'app/site/polls/models/view-base-poll';
|
import { PollService, PollTableData, VotingResult } from 'app/site/polls/services/poll.service';
|
||||||
import { PollService } from 'app/site/polls/services/poll.service';
|
|
||||||
import { AssignmentPollDialogService } from '../../services/assignment-poll-dialog.service';
|
import { AssignmentPollDialogService } from '../../services/assignment-poll-dialog.service';
|
||||||
|
import { AssignmentPollService } from '../../services/assignment-poll.service';
|
||||||
import { ViewAssignmentPoll } from '../../models/view-assignment-poll';
|
import { ViewAssignmentPoll } from '../../models/view-assignment-poll';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -48,7 +48,8 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
|||||||
pollDialog: AssignmentPollDialogService,
|
pollDialog: AssignmentPollDialogService,
|
||||||
pollService: PollService,
|
pollService: PollService,
|
||||||
votesRepo: AssignmentVoteRepositoryService,
|
votesRepo: AssignmentVoteRepositoryService,
|
||||||
private operator: OperatorService
|
private operator: OperatorService,
|
||||||
|
private assignmentPollService: AssignmentPollService
|
||||||
) {
|
) {
|
||||||
super(title, translate, matSnackbar, repo, route, groupRepo, prompt, pollDialog, pollService, votesRepo);
|
super(title, translate, matSnackbar, repo, route, groupRepo, prompt, pollDialog, pollService, votesRepo);
|
||||||
}
|
}
|
||||||
@ -133,4 +134,8 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getTableData(): PollTableData[] {
|
||||||
|
return this.assignmentPollService.generateTableData(this.poll);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
} from 'app/shared/models/assignments/assignment-poll';
|
} from 'app/shared/models/assignments/assignment-poll';
|
||||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||||
import { PollClassType, PollTableData, ViewBasePoll, VotingResult } from 'app/site/polls/models/view-base-poll';
|
import { PollClassType, ViewBasePoll } from 'app/site/polls/models/view-base-poll';
|
||||||
import { ViewAssignment } from './view-assignment';
|
import { ViewAssignment } from './view-assignment';
|
||||||
import { ViewAssignmentOption } from './view-assignment-option';
|
import { ViewAssignmentOption } from './view-assignment-option';
|
||||||
|
|
||||||
@ -39,19 +39,6 @@ export class ViewAssignmentPoll extends ViewBasePoll<AssignmentPoll, AssignmentP
|
|||||||
public readonly tableChartData: Map<string, BehaviorSubject<ChartData>> = new Map();
|
public readonly tableChartData: Map<string, BehaviorSubject<ChartData>> = new Map();
|
||||||
public readonly pollClassType = PollClassType.Assignment;
|
public readonly pollClassType = PollClassType.Assignment;
|
||||||
|
|
||||||
protected globalVoteKeys: VotingResult[] = [
|
|
||||||
{
|
|
||||||
vote: 'amount_global_no',
|
|
||||||
showPercent: false,
|
|
||||||
hide: this.poll.amount_global_no === -2 || this.poll.amount_global_no === 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
vote: 'amount_global_abstain',
|
|
||||||
showPercent: false,
|
|
||||||
hide: this.poll.amount_global_abstain === -2 || this.poll.amount_global_abstain === 0
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
public get pollmethodVerbose(): string {
|
public get pollmethodVerbose(): string {
|
||||||
return AssignmentPollMethodVerbose[this.pollmethod];
|
return AssignmentPollMethodVerbose[this.pollmethod];
|
||||||
}
|
}
|
||||||
@ -77,62 +64,6 @@ export class ViewAssignmentPoll extends ViewBasePoll<AssignmentPoll, AssignmentP
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public generateTableData(): PollTableData[] {
|
|
||||||
const tableData: PollTableData[] = this.options.map(candidate => ({
|
|
||||||
votingOption: candidate.user.short_name,
|
|
||||||
votingOptionSubtitle: candidate.user.getLevelAndNumber(),
|
|
||||||
class: 'user',
|
|
||||||
value: this.voteTableKeys.map(
|
|
||||||
key =>
|
|
||||||
({
|
|
||||||
vote: key.vote,
|
|
||||||
amount: candidate[key.vote],
|
|
||||||
icon: key.icon,
|
|
||||||
hide: key.hide,
|
|
||||||
showPercent: key.showPercent
|
|
||||||
} as VotingResult)
|
|
||||||
)
|
|
||||||
}));
|
|
||||||
|
|
||||||
tableData.push(
|
|
||||||
...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
|
|
||||||
]
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
|
|
||||||
tableData.push(
|
|
||||||
...this.globalVoteKeys
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getDecimalFields(): string[] {
|
protected getDecimalFields(): string[] {
|
||||||
return AssignmentPoll.DECIMAL_FIELDS;
|
return AssignmentPoll.DECIMAL_FIELDS;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,8 @@ import { HtmlToPdfService } from 'app/core/pdf-services/html-to-pdf.service';
|
|||||||
import { ParsePollNumberPipe } from 'app/shared/pipes/parse-poll-number.pipe';
|
import { ParsePollNumberPipe } from 'app/shared/pipes/parse-poll-number.pipe';
|
||||||
import { PollKeyVerbosePipe } from 'app/shared/pipes/poll-key-verbose.pipe';
|
import { PollKeyVerbosePipe } from 'app/shared/pipes/poll-key-verbose.pipe';
|
||||||
import { PollPercentBasePipe } from 'app/shared/pipes/poll-percent-base.pipe';
|
import { PollPercentBasePipe } from 'app/shared/pipes/poll-percent-base.pipe';
|
||||||
import { PollTableData } from 'app/site/polls/models/view-base-poll';
|
import { PollTableData } from 'app/site/polls/services/poll.service';
|
||||||
|
import { AssignmentPollService } from './assignment-poll.service';
|
||||||
import { ViewAssignment } from '../models/view-assignment';
|
import { ViewAssignment } from '../models/view-assignment';
|
||||||
import { ViewAssignmentPoll } from '../models/view-assignment-poll';
|
import { ViewAssignmentPoll } from '../models/view-assignment-poll';
|
||||||
|
|
||||||
@ -30,7 +31,8 @@ export class AssignmentPdfService {
|
|||||||
private htmlToPdfService: HtmlToPdfService,
|
private htmlToPdfService: HtmlToPdfService,
|
||||||
private pollKeyVerbose: PollKeyVerbosePipe,
|
private pollKeyVerbose: PollKeyVerbosePipe,
|
||||||
private parsePollNumber: ParsePollNumberPipe,
|
private parsePollNumber: ParsePollNumberPipe,
|
||||||
private pollPercentBase: PollPercentBasePipe
|
private pollPercentBase: PollPercentBasePipe,
|
||||||
|
private assignmentPollService: AssignmentPollService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -182,7 +184,7 @@ export class AssignmentPdfService {
|
|||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const tableData = poll.generateTableData();
|
const tableData = this.assignmentPollService.generateTableData(poll);
|
||||||
|
|
||||||
for (const pollResult of tableData) {
|
for (const pollResult of tableData) {
|
||||||
const voteOption = this.translate.instant(this.pollKeyVerbose.transform(pollResult.votingOption));
|
const voteOption = this.translate.instant(this.pollKeyVerbose.transform(pollResult.votingOption));
|
||||||
|
@ -11,7 +11,8 @@ import {
|
|||||||
AssignmentPollPercentBase
|
AssignmentPollPercentBase
|
||||||
} from 'app/shared/models/assignments/assignment-poll';
|
} from 'app/shared/models/assignments/assignment-poll';
|
||||||
import { MajorityMethod } from 'app/shared/models/poll/base-poll';
|
import { MajorityMethod } from 'app/shared/models/poll/base-poll';
|
||||||
import { PollData, PollService } from 'app/site/polls/services/poll.service';
|
import { PollData, PollService, PollTableData, VotingResult } from 'app/site/polls/services/poll.service';
|
||||||
|
import { ViewAssignmentPoll } from '../models/view-assignment-poll';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -72,6 +73,60 @@ export class AssignmentPollService extends PollService {
|
|||||||
return poll;
|
return poll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getGlobalVoteKeys(poll: ViewAssignmentPoll): VotingResult[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
vote: 'amount_global_no',
|
||||||
|
showPercent: false,
|
||||||
|
hide: poll.amount_global_no === -2 || poll.amount_global_no === 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
vote: 'amount_global_abstain',
|
||||||
|
showPercent: false,
|
||||||
|
hide: poll.amount_global_abstain === -2 || poll.amount_global_abstain === 0
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public generateTableData(poll: ViewAssignmentPoll): PollTableData[] {
|
||||||
|
const tableData: PollTableData[] = poll.options.map(candidate => ({
|
||||||
|
votingOption: candidate.user.short_name,
|
||||||
|
votingOptionSubtitle: candidate.user.getLevelAndNumber(),
|
||||||
|
class: 'user',
|
||||||
|
value: super.getVoteTableKeys(poll).map(
|
||||||
|
key =>
|
||||||
|
({
|
||||||
|
vote: key.vote,
|
||||||
|
amount: candidate[key.vote],
|
||||||
|
icon: key.icon,
|
||||||
|
hide: key.hide,
|
||||||
|
showPercent: key.showPercent
|
||||||
|
} as VotingResult)
|
||||||
|
)
|
||||||
|
}));
|
||||||
|
tableData.push(...this.formatVotingResultToTableData(super.getSumTableKeys(poll), poll));
|
||||||
|
tableData.push(...this.formatVotingResultToTableData(this.getGlobalVoteKeys(poll), poll));
|
||||||
|
return tableData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private formatVotingResultToTableData(resultList: VotingResult[], poll: PollData): PollTableData[] {
|
||||||
|
return resultList
|
||||||
|
.filter(key => {
|
||||||
|
return !key.hide;
|
||||||
|
})
|
||||||
|
.map(key => ({
|
||||||
|
votingOption: key.vote,
|
||||||
|
class: 'sums',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
amount: poll[key.vote],
|
||||||
|
hide: key.hide,
|
||||||
|
showPercent: key.showPercent
|
||||||
|
} as VotingResult
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
private sumOptionsYN(poll: PollData): number {
|
private sumOptionsYN(poll: PollData): number {
|
||||||
return poll.options.reduce((o, n) => {
|
return poll.options.reduce((o, n) => {
|
||||||
o += n.yes > 0 ? n.yes : 0;
|
o += n.yes > 0 ? n.yes : 0;
|
||||||
|
@ -3,7 +3,7 @@ import { PercentBase } from 'app/shared/models/poll/base-poll';
|
|||||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||||
import { ViewMotionOption } from 'app/site/motions/models/view-motion-option';
|
import { ViewMotionOption } from 'app/site/motions/models/view-motion-option';
|
||||||
import { PollClassType, PollTableData, ViewBasePoll, VotingResult } from 'app/site/polls/models/view-base-poll';
|
import { PollClassType, ViewBasePoll } from 'app/site/polls/models/view-base-poll';
|
||||||
import { ViewMotion } from './view-motion';
|
import { ViewMotion } from './view-motion';
|
||||||
|
|
||||||
export interface MotionPollTitleInformation {
|
export interface MotionPollTitleInformation {
|
||||||
@ -34,10 +34,6 @@ export class ViewMotionPoll extends ViewBasePoll<MotionPoll, MotionPollMethod, P
|
|||||||
return this.options[0];
|
return this.options[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public get hasPresentableValues(): boolean {
|
|
||||||
return this.result.hasPresentableValues;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get hasVotes(): boolean {
|
public get hasVotes(): boolean {
|
||||||
return this.result && !!this.result.votes.length;
|
return this.result && !!this.result.votes.length;
|
||||||
}
|
}
|
||||||
@ -46,31 +42,6 @@ export class ViewMotionPoll extends ViewBasePoll<MotionPoll, MotionPollMethod, P
|
|||||||
return this.motion;
|
return this.motion;
|
||||||
}
|
}
|
||||||
|
|
||||||
public generateTableData(): PollTableData[] {
|
|
||||||
let tableData: PollTableData[] = this.options.flatMap(vote =>
|
|
||||||
this.voteTableKeys.map(key => this.createTableDataEntry(key, vote))
|
|
||||||
);
|
|
||||||
tableData.push(...this.sumTableKeys.map(key => this.createTableDataEntry(key)));
|
|
||||||
|
|
||||||
tableData = tableData.filter(localeTableData => !localeTableData.value.some(result => result.hide));
|
|
||||||
|
|
||||||
return tableData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private createTableDataEntry(result: VotingResult, vote?: ViewMotionOption): PollTableData {
|
|
||||||
return {
|
|
||||||
votingOption: result.vote,
|
|
||||||
value: [
|
|
||||||
{
|
|
||||||
amount: vote ? vote[result.vote] : this[result.vote],
|
|
||||||
hide: result.hide,
|
|
||||||
icon: result.icon,
|
|
||||||
showPercent: result.showPercent
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public getSlide(): ProjectorElementBuildDeskriptor {
|
public getSlide(): ProjectorElementBuildDeskriptor {
|
||||||
return {
|
return {
|
||||||
getBasicProjectorElement: options => ({
|
getBasicProjectorElement: options => ({
|
||||||
|
@ -32,50 +32,7 @@
|
|||||||
<div *ngIf="!poll.hasVotes || !poll.stateHasVotes">{{ 'No results to show' | translate }}</div>
|
<div *ngIf="!poll.hasVotes || !poll.stateHasVotes">{{ 'No results to show' | translate }}</div>
|
||||||
|
|
||||||
<div *ngIf="poll.stateHasVotes">
|
<div *ngIf="poll.stateHasVotes">
|
||||||
<div class="result-wrapper" *ngIf="poll.hasVotes">
|
<os-motion-poll-detail-content [poll]="poll" [chartData]="chartDataSubject"> </os-motion-poll-detail-content>
|
||||||
<!-- result table -->
|
|
||||||
<table class="result-table">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th></th>
|
|
||||||
<th colspan="2" translate>Votes</th>
|
|
||||||
</tr>
|
|
||||||
<tr *ngFor="let row of poll.tableData" [class]="row.votingOption">
|
|
||||||
<!-- YNA/Valid etc -->
|
|
||||||
<td>
|
|
||||||
<os-icon-container *ngIf="row.value[0].icon" [icon]="row.value[0].icon">
|
|
||||||
{{ row.votingOption | pollKeyVerbose | translate }}
|
|
||||||
</os-icon-container>
|
|
||||||
<span *ngIf="!row.value[0].icon">
|
|
||||||
{{ row.votingOption | pollKeyVerbose | translate }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<!-- Percent numbers -->
|
|
||||||
<td class="result-cell-definition">
|
|
||||||
<span *ngIf="row.value[0].showPercent">
|
|
||||||
{{ row.value[0].amount | pollPercentBase: poll }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<!-- Voices -->
|
|
||||||
<td class="result-cell-definition">
|
|
||||||
{{ row.value[0].amount | parsePollNumber }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<!-- Chart -->
|
|
||||||
<div class="doughnut-chart" *ngIf="poll.hasPresentableValues && chartDataSubject.value">
|
|
||||||
<os-charts
|
|
||||||
[type]="chartType"
|
|
||||||
[data]="chartDataSubject"
|
|
||||||
[showLegend]="false"
|
|
||||||
[hasPadding]="false"
|
|
||||||
></os-charts>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Named table: only show if votes are present -->
|
<!-- Named table: only show if votes are present -->
|
||||||
<div class="named-result-table" *ngIf="poll.type === 'named'">
|
<div class="named-result-table" *ngIf="poll.type === 'named'">
|
||||||
|
@ -7,48 +7,6 @@
|
|||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-wrapper {
|
|
||||||
display: grid;
|
|
||||||
grid-gap: 2em;
|
|
||||||
margin: 2em;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
||||||
|
|
||||||
.result-table {
|
|
||||||
// display: block;
|
|
||||||
th {
|
|
||||||
text-align: right;
|
|
||||||
font-weight: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr {
|
|
||||||
height: 48px;
|
|
||||||
border-bottom: none !important;
|
|
||||||
|
|
||||||
.result-cell-definition {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.yes {
|
|
||||||
color: $votes-yes-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no {
|
|
||||||
color: $votes-no-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.abstain {
|
|
||||||
color: $votes-abstain-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.doughnut-chart {
|
|
||||||
display: block;
|
|
||||||
margin-top: auto;
|
|
||||||
margin-bottom: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.named-result-table {
|
.named-result-table {
|
||||||
grid-area: names;
|
grid-area: names;
|
||||||
.mat-form-field {
|
.mat-form-field {
|
||||||
@ -72,13 +30,6 @@
|
|||||||
height: 500px;
|
height: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.openslides-theme os-list-view-table os-sort-filter-bar .custom-table-header {
|
|
||||||
&,
|
|
||||||
.action-buttons .input-container input {
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.voted-yes {
|
.voted-yes {
|
||||||
color: $votes-yes-color;
|
color: $votes-yes-color;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,12 @@
|
|||||||
@import '~@angular/material/theming';
|
@import '~@angular/material/theming';
|
||||||
|
|
||||||
@mixin os-motion-poll-detail-style($theme) {
|
@mixin os-motion-poll-detail-style($theme) {
|
||||||
|
$background: map-get($theme, background);
|
||||||
|
|
||||||
|
os-list-view-table os-sort-filter-bar .custom-table-header {
|
||||||
|
&,
|
||||||
|
.action-buttons .input-container input {
|
||||||
|
background: mat-color($background, card);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import { MotionPollRepositoryService } from 'app/core/repositories/motions/motio
|
|||||||
import { MotionVoteRepositoryService } from 'app/core/repositories/motions/motion-vote-repository.service';
|
import { MotionVoteRepositoryService } from 'app/core/repositories/motions/motion-vote-repository.service';
|
||||||
import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { ChartType } from 'app/shared/components/charts/charts.component';
|
|
||||||
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
||||||
import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
||||||
import { MotionPollDialogService } from 'app/site/motions/services/motion-poll-dialog.service';
|
import { MotionPollDialogService } from 'app/site/motions/services/motion-poll-dialog.service';
|
||||||
@ -38,18 +37,9 @@ export class MotionPollDetailComponent extends BasePollDetailComponent<ViewMotio
|
|||||||
label: 'Vote'
|
label: 'Vote'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
public filterProps = ['user.getFullName', 'valueVerbose'];
|
public filterProps = ['user.getFullName', 'valueVerbose'];
|
||||||
|
|
||||||
public set chartType(type: ChartType) {
|
|
||||||
this._chartType = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get chartType(): ChartType {
|
|
||||||
return this._chartType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _chartType: ChartType = 'doughnut';
|
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
title: Title,
|
title: Title,
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
@ -60,9 +50,9 @@ export class MotionPollDetailComponent extends BasePollDetailComponent<ViewMotio
|
|||||||
prompt: PromptService,
|
prompt: PromptService,
|
||||||
pollDialog: MotionPollDialogService,
|
pollDialog: MotionPollDialogService,
|
||||||
pollService: PollService,
|
pollService: PollService,
|
||||||
|
votesRepo: MotionVoteRepositoryService,
|
||||||
private operator: OperatorService,
|
private operator: OperatorService,
|
||||||
private router: Router,
|
private router: Router
|
||||||
votesRepo: MotionVoteRepositoryService
|
|
||||||
) {
|
) {
|
||||||
super(title, translate, matSnackbar, repo, route, groupRepo, prompt, pollDialog, pollService, votesRepo);
|
super(title, translate, matSnackbar, repo, route, groupRepo, prompt, pollDialog, pollService, votesRepo);
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,9 @@ import { infoDialogSettings } from 'app/shared/utils/dialog-settings';
|
|||||||
import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
||||||
import { MotionPollDialogService } from 'app/site/motions/services/motion-poll-dialog.service';
|
import { MotionPollDialogService } from 'app/site/motions/services/motion-poll-dialog.service';
|
||||||
import { MotionPollPdfService } from 'app/site/motions/services/motion-poll-pdf.service';
|
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 { BasePollComponent } from 'app/site/polls/components/base-poll.component';
|
||||||
import { PollTableData } from 'app/site/polls/models/view-base-poll';
|
import { PollService, PollTableData } from 'app/site/polls/services/poll.service';
|
||||||
import { PollService } from 'app/site/polls/services/poll.service';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component to show a motion-poll.
|
* Component to show a motion-poll.
|
||||||
@ -45,7 +45,7 @@ export class MotionPollComponent extends BasePollComponent<ViewMotionPoll> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get showChart(): boolean {
|
public get showChart(): boolean {
|
||||||
return this.poll.hasPresentableValues;
|
return this.motionPollService.showChart(this.poll);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get hideChangeState(): boolean {
|
public get hideChangeState(): boolean {
|
||||||
@ -53,7 +53,9 @@ export class MotionPollComponent extends BasePollComponent<ViewMotionPoll> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get reducedPollTableData(): PollTableData[] {
|
public get reducedPollTableData(): PollTableData[] {
|
||||||
return this.poll.tableData.filter(data => ['yes', 'no', 'abstain', 'votesinvalid'].includes(data.votingOption));
|
return this.motionPollService
|
||||||
|
.generateTableData(this.poll)
|
||||||
|
.filter(data => ['yes', 'no', 'abstain', 'votesinvalid'].includes(data.votingOption));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,7 +76,8 @@ export class MotionPollComponent extends BasePollComponent<ViewMotionPoll> {
|
|||||||
public pollRepo: MotionPollRepositoryService,
|
public pollRepo: MotionPollRepositoryService,
|
||||||
pollDialog: MotionPollDialogService,
|
pollDialog: MotionPollDialogService,
|
||||||
public pollService: PollService,
|
public pollService: PollService,
|
||||||
private pdfService: MotionPollPdfService
|
private pdfService: MotionPollPdfService,
|
||||||
|
private motionPollService: MotionPollService
|
||||||
) {
|
) {
|
||||||
super(titleService, matSnackBar, translate, dialog, promptService, pollRepo, pollDialog);
|
super(titleService, matSnackBar, translate, dialog, promptService, pollRepo, pollDialog);
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import { PollKeyVerbosePipe } from 'app/shared/pipes/poll-key-verbose.pipe';
|
|||||||
import { PollPercentBasePipe } from 'app/shared/pipes/poll-percent-base.pipe';
|
import { PollPercentBasePipe } from 'app/shared/pipes/poll-percent-base.pipe';
|
||||||
import { getRecommendationTypeName } from 'app/shared/utils/recommendation-type-names';
|
import { getRecommendationTypeName } from 'app/shared/utils/recommendation-type-names';
|
||||||
import { MotionExportInfo } from './motion-export.service';
|
import { MotionExportInfo } from './motion-export.service';
|
||||||
|
import { MotionPollService } from './motion-poll.service';
|
||||||
import { ChangeRecoMode, InfoToExport, LineNumberingMode, PERSONAL_NOTE_ID } from '../motions.constants';
|
import { ChangeRecoMode, InfoToExport, LineNumberingMode, PERSONAL_NOTE_ID } from '../motions.constants';
|
||||||
import { ViewMotion } from '../models/view-motion';
|
import { ViewMotion } from '../models/view-motion';
|
||||||
import { ViewMotionAmendedParagraph } from '../models/view-motion-amended-paragraph';
|
import { ViewMotionAmendedParagraph } from '../models/view-motion-amended-paragraph';
|
||||||
@ -67,7 +68,8 @@ export class MotionPdfService {
|
|||||||
private commentRepo: MotionCommentSectionRepositoryService,
|
private commentRepo: MotionCommentSectionRepositoryService,
|
||||||
private pollKeyVerbose: PollKeyVerbosePipe,
|
private pollKeyVerbose: PollKeyVerbosePipe,
|
||||||
private pollPercentBase: PollPercentBasePipe,
|
private pollPercentBase: PollPercentBasePipe,
|
||||||
private parsePollNumber: ParsePollNumberPipe
|
private parsePollNumber: ParsePollNumberPipe,
|
||||||
|
private motionPollService: MotionPollService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -370,7 +372,7 @@ export class MotionPdfService {
|
|||||||
const column3 = [];
|
const column3 = [];
|
||||||
motion.polls.forEach(poll => {
|
motion.polls.forEach(poll => {
|
||||||
if (poll.hasVotes) {
|
if (poll.hasVotes) {
|
||||||
const tableData = poll.generateTableData();
|
const tableData = this.motionPollService.generateTableData(poll);
|
||||||
|
|
||||||
tableData.forEach(votingResult => {
|
tableData.forEach(votingResult => {
|
||||||
const votingOption = this.translate.instant(
|
const votingOption = this.translate.instant(
|
||||||
|
@ -7,7 +7,9 @@ import { MotionPollRepositoryService } from 'app/core/repositories/motions/motio
|
|||||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
import { MotionPoll, MotionPollMethod } from 'app/shared/models/motions/motion-poll';
|
import { MotionPoll, MotionPollMethod } from 'app/shared/models/motions/motion-poll';
|
||||||
import { MajorityMethod, PercentBase } from 'app/shared/models/poll/base-poll';
|
import { MajorityMethod, PercentBase } from 'app/shared/models/poll/base-poll';
|
||||||
import { PollData, PollService } from 'app/site/polls/services/poll.service';
|
import { PollData, PollService, PollTableData, VotingResult } from 'app/site/polls/services/poll.service';
|
||||||
|
import { ViewMotionOption } from '../models/view-motion-option';
|
||||||
|
import { ViewMotionPoll } from '../models/view-motion-poll';
|
||||||
|
|
||||||
interface PollResultData {
|
interface PollResultData {
|
||||||
yes?: number;
|
yes?: number;
|
||||||
@ -71,6 +73,38 @@ export class MotionPollService extends PollService {
|
|||||||
return poll;
|
return poll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public generateTableData(poll: PollData | ViewMotionPoll): PollTableData[] {
|
||||||
|
let tableData: PollTableData[] = poll.options.flatMap(vote =>
|
||||||
|
super.getVoteTableKeys(poll).map(key => this.createTableDataEntry(poll, key, vote))
|
||||||
|
);
|
||||||
|
tableData.push(...super.getSumTableKeys(poll).map(key => this.createTableDataEntry(poll, key)));
|
||||||
|
|
||||||
|
tableData = tableData.filter(localeTableData => !localeTableData.value.some(result => result.hide));
|
||||||
|
return tableData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createTableDataEntry(
|
||||||
|
poll: PollData | ViewMotionPoll,
|
||||||
|
result: VotingResult,
|
||||||
|
vote?: ViewMotionOption
|
||||||
|
): PollTableData {
|
||||||
|
return {
|
||||||
|
votingOption: result.vote,
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
amount: vote ? vote[result.vote] : poll[result.vote],
|
||||||
|
hide: result.hide,
|
||||||
|
icon: result.icon,
|
||||||
|
showPercent: result.showPercent
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public showChart(poll: PollData): boolean {
|
||||||
|
return poll && poll.options && poll.options.some(option => option.yes >= 0 && option.no >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
public getPercentBase(poll: PollData): number {
|
public getPercentBase(poll: PollData): number {
|
||||||
const base: PercentBase = poll.onehundred_percent_base as PercentBase;
|
const base: PercentBase = poll.onehundred_percent_base as PercentBase;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import { BaseRepository } from 'app/core/repositories/base-repository';
|
|||||||
import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
||||||
import { BasePollDialogService } from 'app/core/ui-services/base-poll-dialog.service';
|
import { BasePollDialogService } from 'app/core/ui-services/base-poll-dialog.service';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { ChartData, ChartType } from 'app/shared/components/charts/charts.component';
|
import { ChartData } from 'app/shared/components/charts/charts.component';
|
||||||
import { BaseVote } from 'app/shared/models/poll/base-vote';
|
import { BaseVote } from 'app/shared/models/poll/base-vote';
|
||||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
import { ViewGroup } from 'app/site/users/models/view-group';
|
import { ViewGroup } from 'app/site/users/models/view-group';
|
||||||
@ -60,11 +60,6 @@ export abstract class BasePollDetailComponent<V extends ViewBasePoll> extends Ba
|
|||||||
*/
|
*/
|
||||||
public poll: V = null;
|
public poll: V = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the type of the shown chart, if votes are entered.
|
|
||||||
*/
|
|
||||||
public abstract get chartType(): ChartType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The different labels for the votes (used for chart).
|
* The different labels for the votes (used for chart).
|
||||||
*/
|
*/
|
||||||
|
@ -4,10 +4,6 @@ import { ViewBasePoll } from './view-base-poll';
|
|||||||
import { ViewBaseVote } from './view-base-vote';
|
import { ViewBaseVote } from './view-base-vote';
|
||||||
|
|
||||||
export class ViewBaseOption<M extends BaseOption<M> = any> extends BaseViewModel<M> {
|
export class ViewBaseOption<M extends BaseOption<M> = any> extends BaseViewModel<M> {
|
||||||
public get hasPresentableValues(): boolean {
|
|
||||||
return this.yes >= 0 && this.no >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get option(): M {
|
public get option(): M {
|
||||||
return this._model;
|
return this._model;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { BasePoll, PercentBase, PollType } from 'app/shared/models/poll/base-poll';
|
import { BasePoll } from 'app/shared/models/poll/base-poll';
|
||||||
import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model';
|
import { BaseProjectableViewModel } from 'app/site/base/base-projectable-view-model';
|
||||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||||
@ -11,32 +11,6 @@ export enum PollClassType {
|
|||||||
Assignment = 'assignment'
|
Assignment = 'assignment'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface describes the possible data for the result-table.
|
|
||||||
*/
|
|
||||||
export interface PollTableData {
|
|
||||||
votingOption: string;
|
|
||||||
votingOptionSubtitle?: string;
|
|
||||||
class?: string;
|
|
||||||
value: VotingResult[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface VotingResult {
|
|
||||||
vote?:
|
|
||||||
| 'yes'
|
|
||||||
| 'no'
|
|
||||||
| 'abstain'
|
|
||||||
| 'votesvalid'
|
|
||||||
| 'votesinvalid'
|
|
||||||
| 'votescast'
|
|
||||||
| 'amount_global_no'
|
|
||||||
| 'amount_global_abstain';
|
|
||||||
amount?: number;
|
|
||||||
icon?: string;
|
|
||||||
hide?: boolean;
|
|
||||||
showPercent?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PollClassTypeVerbose = {
|
export const PollClassTypeVerbose = {
|
||||||
motion: 'Motion poll',
|
motion: 'Motion poll',
|
||||||
assignment: 'Assignment poll'
|
assignment: 'Assignment poll'
|
||||||
@ -94,52 +68,6 @@ export abstract class ViewBasePoll<
|
|||||||
PM extends string = string,
|
PM extends string = string,
|
||||||
PB extends string = string
|
PB extends string = string
|
||||||
> extends BaseProjectableViewModel<M> {
|
> extends BaseProjectableViewModel<M> {
|
||||||
private _tableData: PollTableData[] = [];
|
|
||||||
|
|
||||||
protected voteTableKeys: VotingResult[] = [
|
|
||||||
{
|
|
||||||
vote: 'yes',
|
|
||||||
icon: 'thumb_up',
|
|
||||||
showPercent: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
vote: 'no',
|
|
||||||
icon: 'thumb_down',
|
|
||||||
showPercent: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
vote: 'abstain',
|
|
||||||
icon: 'trip_origin',
|
|
||||||
showPercent: this.showAbstainPercent
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
protected sumTableKeys: VotingResult[] = [
|
|
||||||
{
|
|
||||||
vote: 'votesvalid',
|
|
||||||
hide: this.poll.votesvalid === -2,
|
|
||||||
showPercent: this.poll.isPercentBaseValidOrCast
|
|
||||||
},
|
|
||||||
{
|
|
||||||
vote: 'votesinvalid',
|
|
||||||
icon: 'not_interested',
|
|
||||||
hide: this.poll.type !== PollType.Analog || this.poll.votesinvalid === -2,
|
|
||||||
showPercent: this.poll.isPercentBaseCast
|
|
||||||
},
|
|
||||||
{
|
|
||||||
vote: 'votescast',
|
|
||||||
hide: this.poll.type !== PollType.Analog || this.poll.votescast === -2,
|
|
||||||
showPercent: this.poll.isPercentBaseCast
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
public get tableData(): PollTableData[] {
|
|
||||||
if (!this._tableData.length) {
|
|
||||||
this._tableData = this.generateTableData();
|
|
||||||
}
|
|
||||||
return this._tableData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get poll(): M {
|
public get poll(): M {
|
||||||
return this._model;
|
return this._model;
|
||||||
}
|
}
|
||||||
@ -172,14 +100,6 @@ export abstract class ViewBasePoll<
|
|||||||
|
|
||||||
public abstract get percentBaseVerbose(): string;
|
public abstract get percentBaseVerbose(): string;
|
||||||
|
|
||||||
public get showAbstainPercent(): boolean {
|
|
||||||
return (
|
|
||||||
this.poll.onehundred_percent_base === PercentBase.YNA ||
|
|
||||||
this.poll.onehundred_percent_base === PercentBase.Valid ||
|
|
||||||
this.poll.onehundred_percent_base === PercentBase.Cast
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract readonly pollClassType: 'motion' | 'assignment';
|
public abstract readonly pollClassType: 'motion' | 'assignment';
|
||||||
|
|
||||||
public canBeVotedFor: () => boolean;
|
public canBeVotedFor: () => boolean;
|
||||||
@ -187,8 +107,6 @@ export abstract class ViewBasePoll<
|
|||||||
public abstract getSlide(): ProjectorElementBuildDeskriptor;
|
public abstract getSlide(): ProjectorElementBuildDeskriptor;
|
||||||
|
|
||||||
public abstract getContentObject(): BaseViewModel;
|
public abstract getContentObject(): BaseViewModel;
|
||||||
|
|
||||||
public abstract generateTableData(): PollTableData[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ViewBasePoll<
|
export interface ViewBasePoll<
|
||||||
|
@ -9,7 +9,8 @@ import {
|
|||||||
MajorityMethodVerbose,
|
MajorityMethodVerbose,
|
||||||
PercentBaseVerbose,
|
PercentBaseVerbose,
|
||||||
PollPropertyVerbose,
|
PollPropertyVerbose,
|
||||||
PollTypeVerbose
|
PollTypeVerbose,
|
||||||
|
ViewBasePoll
|
||||||
} from 'app/site/polls/models/view-base-poll';
|
} from 'app/site/polls/models/view-base-poll';
|
||||||
import { ConstantsService } from '../../../core/core-services/constants.service';
|
import { ConstantsService } from '../../../core/core-services/constants.service';
|
||||||
|
|
||||||
@ -108,6 +109,32 @@ interface OpenSlidesSettings {
|
|||||||
ENABLE_ELECTRONIC_VOTING: boolean;
|
ENABLE_ELECTRONIC_VOTING: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface describes the possible data for the result-table.
|
||||||
|
*/
|
||||||
|
export interface PollTableData {
|
||||||
|
votingOption: string;
|
||||||
|
votingOptionSubtitle?: string;
|
||||||
|
class?: string;
|
||||||
|
value: VotingResult[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VotingResult {
|
||||||
|
vote?:
|
||||||
|
| 'yes'
|
||||||
|
| 'no'
|
||||||
|
| 'abstain'
|
||||||
|
| 'votesvalid'
|
||||||
|
| 'votesinvalid'
|
||||||
|
| 'votescast'
|
||||||
|
| 'amount_global_no'
|
||||||
|
| 'amount_global_abstain';
|
||||||
|
amount?: number;
|
||||||
|
icon?: string;
|
||||||
|
hide?: boolean;
|
||||||
|
showPercent?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shared service class for polls. Used by child classes {@link MotionPollService}
|
* Shared service class for polls. Used by child classes {@link MotionPollService}
|
||||||
* and {@link AssignmentPollService}
|
* and {@link AssignmentPollService}
|
||||||
@ -179,7 +206,58 @@ export abstract class PollService {
|
|||||||
return PollPropertyVerbose[key];
|
return PollPropertyVerbose[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
public generateChartData(poll: PollData): ChartData {
|
public getVoteTableKeys(poll: PollData | ViewBasePoll): VotingResult[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
vote: 'yes',
|
||||||
|
icon: 'thumb_up',
|
||||||
|
showPercent: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
vote: 'no',
|
||||||
|
icon: 'thumb_down',
|
||||||
|
showPercent: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
vote: 'abstain',
|
||||||
|
icon: 'trip_origin',
|
||||||
|
showPercent: this.showAbstainPercent(poll)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private showAbstainPercent(poll: PollData | ViewBasePoll): boolean {
|
||||||
|
return (
|
||||||
|
poll.onehundred_percent_base === PercentBase.YNA ||
|
||||||
|
poll.onehundred_percent_base === PercentBase.Valid ||
|
||||||
|
poll.onehundred_percent_base === PercentBase.Cast
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSumTableKeys(poll: PollData | ViewBasePoll): VotingResult[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
vote: 'votesvalid',
|
||||||
|
hide: poll.votesvalid === -2,
|
||||||
|
showPercent:
|
||||||
|
poll.onehundred_percent_base === PercentBase.Valid ||
|
||||||
|
poll.onehundred_percent_base === PercentBase.Cast
|
||||||
|
},
|
||||||
|
{
|
||||||
|
vote: 'votesinvalid',
|
||||||
|
icon: 'not_interested',
|
||||||
|
hide: poll.votesinvalid === -2,
|
||||||
|
showPercent: poll.onehundred_percent_base === PercentBase.Cast
|
||||||
|
},
|
||||||
|
{
|
||||||
|
vote: 'votescast',
|
||||||
|
hide: poll.votescast === -2,
|
||||||
|
showPercent: poll.onehundred_percent_base === PercentBase.Cast
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public generateChartData(poll: PollData | ViewBasePoll): ChartData {
|
||||||
let fields: CalculablePollKey[];
|
let fields: CalculablePollKey[];
|
||||||
|
|
||||||
// TODO: PollData should either be `ViewBasePoll` or `BasePoll` to get SOLID
|
// TODO: PollData should either be `ViewBasePoll` or `BasePoll` to get SOLID
|
||||||
|
@ -6,40 +6,11 @@
|
|||||||
</h1>
|
</h1>
|
||||||
<h2 class="poll-title">{{ data.data.poll.title }}</h2>
|
<h2 class="poll-title">{{ data.data.poll.title }}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="poll-chart-wrapper" *ngIf="data.data.poll.state === PollState.Published">
|
<div *ngIf="data.data.poll.state === PollState.Published">
|
||||||
<div class="doughnut-chart">
|
<os-motion-poll-detail-content [poll]="pollData" [chartData]="chartDataSubject"> </os-motion-poll-detail-content>
|
||||||
<os-charts
|
|
||||||
[type]="'doughnut'"
|
|
||||||
[data]="chartDataSubject"
|
|
||||||
[pieChartOptions]="{ maintainAspectRatio: false, responsive: true }"
|
|
||||||
[showLegend]="false"
|
|
||||||
[hasPadding]="false"
|
|
||||||
>
|
|
||||||
</os-charts>
|
|
||||||
</div>
|
|
||||||
<div class="vote-legend">
|
|
||||||
<div class="votes-yes" *ngIf="pollService.isVoteDocumented(voteYes)">
|
|
||||||
<os-icon-container icon="thumb_up" size="large">
|
|
||||||
{{ voteYes | parsePollNumber }}
|
|
||||||
{{ voteYes | pollPercentBase: data.data.poll }}
|
|
||||||
</os-icon-container>
|
|
||||||
</div>
|
|
||||||
<div class="votes-no" *ngIf="pollService.isVoteDocumented(voteNo)">
|
|
||||||
<os-icon-container icon="thumb_down" size="large">
|
|
||||||
{{ voteNo | parsePollNumber }}
|
|
||||||
{{ voteNo | pollPercentBase: data.data.poll }}
|
|
||||||
</os-icon-container>
|
|
||||||
</div>
|
|
||||||
<div class="votes-abstain" *ngIf="pollService.isVoteDocumented(voteAbstain)">
|
|
||||||
<os-icon-container icon="trip_origin" size="large">
|
|
||||||
{{ voteAbstain | parsePollNumber }}
|
|
||||||
{{ voteAbstain | pollPercentBase: data.data.poll }}
|
|
||||||
</os-icon-container>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="data.data.poll.state !== PollState.Published">
|
<div *ngIf="data.data.poll.state !== PollState.Published">
|
||||||
<!-- TODO -->
|
<!-- TODO -->
|
||||||
{{ "Nothing to see here!" | translate }}
|
{{ 'Nothing to see here!' | translate }}
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
@ -3,38 +3,3 @@
|
|||||||
.motion-title {
|
.motion-title {
|
||||||
margin: 0 0 10px;
|
margin: 0 0 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poll-chart-wrapper {
|
|
||||||
display: grid;
|
|
||||||
grid-gap: 10px;
|
|
||||||
grid-template-areas: 'chart legend';
|
|
||||||
grid-template-columns: min-content auto;
|
|
||||||
|
|
||||||
.doughnut-chart {
|
|
||||||
grid-area: chart;
|
|
||||||
margin-top: auto;
|
|
||||||
margin-bottom: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vote-legend {
|
|
||||||
grid-area: legend;
|
|
||||||
margin-top: auto;
|
|
||||||
margin-bottom: auto;
|
|
||||||
|
|
||||||
div + div {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.votes-yes {
|
|
||||||
color: $votes-yes-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.votes-no {
|
|
||||||
color: $votes-no-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.votes-abstain {
|
|
||||||
color: $votes-abstain-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { PollState } from 'app/shared/models/poll/base-poll';
|
import { PollState } from 'app/shared/models/poll/base-poll';
|
||||||
import { PollService } from 'app/site/polls/services/poll.service';
|
import { MotionPollService } from 'app/site/motions/services/motion-poll.service';
|
||||||
|
import { PollData, PollService, PollTableData } from 'app/site/polls/services/poll.service';
|
||||||
import { BasePollSlideComponent } from 'app/slides/polls/base-poll-slide.component';
|
import { BasePollSlideComponent } from 'app/slides/polls/base-poll-slide.component';
|
||||||
import { MotionPollSlideData } from './motion-poll-slide-data';
|
import { MotionPollSlideData } from './motion-poll-slide-data';
|
||||||
|
|
||||||
@ -13,19 +14,29 @@ import { MotionPollSlideData } from './motion-poll-slide-data';
|
|||||||
export class MotionPollSlideComponent extends BasePollSlideComponent<MotionPollSlideData> {
|
export class MotionPollSlideComponent extends BasePollSlideComponent<MotionPollSlideData> {
|
||||||
public PollState = PollState;
|
public PollState = PollState;
|
||||||
|
|
||||||
|
public pollData: PollData;
|
||||||
public voteYes: number;
|
public voteYes: number;
|
||||||
public voteNo: number;
|
public voteNo: number;
|
||||||
public voteAbstain: number;
|
public voteAbstain: number;
|
||||||
|
|
||||||
public constructor(pollService: PollService) {
|
public constructor(pollService: PollService, private motionPollService: MotionPollService) {
|
||||||
super(pollService);
|
super(pollService);
|
||||||
this.chartDataSubject.subscribe(() => {
|
this.chartDataSubject.subscribe(() => {
|
||||||
if (this.data && this.data.data) {
|
if (this.data && this.data.data) {
|
||||||
const result = this.data.data.poll.options[0];
|
this.pollData = this.data.data.poll as PollData;
|
||||||
|
const result = this.pollData.options[0];
|
||||||
this.voteYes = result.yes;
|
this.voteYes = result.yes;
|
||||||
this.voteNo = result.no;
|
this.voteNo = result.no;
|
||||||
this.voteAbstain = result.abstain;
|
this.voteAbstain = result.abstain;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public showChart(): boolean {
|
||||||
|
return this.motionPollService.showChart(this.pollData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTableData(): PollTableData[] {
|
||||||
|
return this.motionPollService.generateTableData(this.pollData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user