Some overall improvements
Common: delete unused motion poll list Poll Create form: Fix ugly multi line mat hints (workaround, see https://github.com/angular/components/issues/5227 ) Poll List: Fix too tiny column size user_has_voted_valid (ceck icon) was not shown Motion Poll Card: Enhance subtitle layout (type + state) Assignment Poll Card: Open warning after clicking the hint icon Assignment Poll Chart: Show Absolute values and percents in chart label Assignment Detail: Add new ballot button with plus icon instead of chart icon
This commit is contained in:
parent
64f2720b1a
commit
3c9f6ed278
@ -21,26 +21,24 @@ import { PollData } from 'app/site/polls/services/poll.service';
|
||||
name: 'pollPercentBase'
|
||||
})
|
||||
export class PollPercentBasePipe implements PipeTransform {
|
||||
private decimalPlaces = 3;
|
||||
|
||||
public constructor(
|
||||
private assignmentPollService: AssignmentPollService,
|
||||
private motionPollService: MotionPollService
|
||||
) {}
|
||||
|
||||
public transform(value: number, poll: PollData): string | null {
|
||||
let totalByBase: number;
|
||||
// logic handles over the pollService to avoid circular dependencies
|
||||
let voteValueInPercent: string;
|
||||
if ((<any>poll).assignment) {
|
||||
totalByBase = this.assignmentPollService.getPercentBase(poll);
|
||||
voteValueInPercent = this.assignmentPollService.getVoteValueInPercent(value, poll);
|
||||
} else {
|
||||
totalByBase = this.motionPollService.getPercentBase(poll);
|
||||
voteValueInPercent = this.motionPollService.getVoteValueInPercent(value, poll);
|
||||
}
|
||||
|
||||
if (totalByBase && totalByBase > 0) {
|
||||
const percentNumber = (value / totalByBase) * 100;
|
||||
const result = percentNumber % 1 === 0 ? percentNumber : percentNumber.toFixed(this.decimalPlaces);
|
||||
return `(${result} %)`;
|
||||
}
|
||||
if (voteValueInPercent) {
|
||||
return `(${voteValueInPercent})`;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +75,7 @@
|
||||
<!-- New Ballot button -->
|
||||
<div class="new-ballot-button" *ngIf="assignment && hasPerms('createPoll')">
|
||||
<button mat-stroked-button (click)="openDialog()">
|
||||
<mat-icon color="primary">poll</mat-icon>
|
||||
<mat-icon>add</mat-icon>
|
||||
<span translate>New ballot</span>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -14,7 +14,7 @@ import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||
import { ChartType } from 'app/shared/components/charts/charts.component';
|
||||
import { VoteValue } from 'app/shared/models/poll/base-vote';
|
||||
import { BasePollDetailComponent } from 'app/site/polls/components/base-poll-detail.component';
|
||||
import { PollService, PollTableData, VotingResult } from 'app/site/polls/services/poll.service';
|
||||
import { PollTableData, VotingResult } from 'app/site/polls/services/poll.service';
|
||||
import { AssignmentPollDialogService } from '../../services/assignment-poll-dialog.service';
|
||||
import { AssignmentPollService } from '../../services/assignment-poll.service';
|
||||
import { ViewAssignmentPoll } from '../../models/view-assignment-poll';
|
||||
@ -25,7 +25,7 @@ import { ViewAssignmentPoll } from '../../models/view-assignment-poll';
|
||||
styleUrls: ['./assignment-poll-detail.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewAssignmentPoll> {
|
||||
export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewAssignmentPoll, AssignmentPollService> {
|
||||
public columnDefinitionSingleVotes: PblColumnDefinition[];
|
||||
|
||||
public filterProps = ['user.getFullName'];
|
||||
@ -47,10 +47,9 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
||||
groupRepo: GroupRepositoryService,
|
||||
prompt: PromptService,
|
||||
pollDialog: AssignmentPollDialogService,
|
||||
pollService: PollService,
|
||||
protected pollService: AssignmentPollService,
|
||||
votesRepo: AssignmentVoteRepositoryService,
|
||||
private operator: OperatorService,
|
||||
private assignmentPollService: AssignmentPollService,
|
||||
private router: Router
|
||||
) {
|
||||
super(title, translate, matSnackbar, repo, route, groupRepo, prompt, pollDialog, pollService, votesRepo);
|
||||
@ -144,11 +143,11 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
||||
return true;
|
||||
}
|
||||
|
||||
public getTableData(): PollTableData[] {
|
||||
return this.pollService.generateTableData(this.poll);
|
||||
}
|
||||
|
||||
protected onDeleted(): void {
|
||||
this.router.navigate(['assignments', this.poll.assignment_id]);
|
||||
}
|
||||
|
||||
public getTableData(): PollTableData[] {
|
||||
return this.assignmentPollService.generateTableData(this.poll);
|
||||
}
|
||||
}
|
||||
|
@ -9,10 +9,12 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
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';
|
||||
import { VotingPrivacyWarningComponent } from 'app/shared/components/voting-privacy-warning/voting-privacy-warning.component';
|
||||
import { infoDialogSettings } from 'app/shared/utils/dialog-settings';
|
||||
import { BasePollComponent } from 'app/site/polls/components/base-poll.component';
|
||||
import { PollService } from 'app/site/polls/services/poll.service';
|
||||
import { AssignmentPollDialogService } from '../../services/assignment-poll-dialog.service';
|
||||
import { AssignmentPollPdfService } from '../../services/assignment-poll-pdf.service';
|
||||
import { AssignmentPollService } from '../../services/assignment-poll.service';
|
||||
import { ViewAssignmentPoll } from '../../models/view-assignment-poll';
|
||||
|
||||
/**
|
||||
@ -62,7 +64,7 @@ export class AssignmentPollComponent extends BasePollComponent<ViewAssignmentPol
|
||||
promptService: PromptService,
|
||||
repo: AssignmentPollRepositoryService,
|
||||
pollDialog: AssignmentPollDialogService,
|
||||
public pollService: PollService,
|
||||
private pollService: AssignmentPollService,
|
||||
private formBuilder: FormBuilder,
|
||||
private pdfService: AssignmentPollPdfService
|
||||
) {
|
||||
@ -81,4 +83,8 @@ export class AssignmentPollComponent extends BasePollComponent<ViewAssignmentPol
|
||||
public printBallot(): void {
|
||||
this.pdfService.printBallots(this.poll);
|
||||
}
|
||||
|
||||
public openVotingWarning(): void {
|
||||
this.dialog.open(VotingPrivacyWarningComponent, infoDialogSettings);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ import {
|
||||
AssignmentPollPercentBase
|
||||
} from 'app/shared/models/assignments/assignment-poll';
|
||||
import { MajorityMethod, VOTE_UNDOCUMENTED } from 'app/shared/models/poll/base-poll';
|
||||
import { ParsePollNumberPipe } from 'app/shared/pipes/parse-poll-number.pipe';
|
||||
import { PollKeyVerbosePipe } from 'app/shared/pipes/poll-key-verbose.pipe';
|
||||
import { PollData, PollService, PollTableData, VotingResult } from 'app/site/polls/services/poll.service';
|
||||
import { ViewAssignmentPoll } from '../models/view-assignment-poll';
|
||||
|
||||
@ -41,10 +43,12 @@ export class AssignmentPollService extends PollService {
|
||||
public constructor(
|
||||
config: ConfigService,
|
||||
constants: ConstantsService,
|
||||
private translate: TranslateService,
|
||||
pollKeyVerbose: PollKeyVerbosePipe,
|
||||
parsePollNumber: ParsePollNumberPipe,
|
||||
protected translate: TranslateService,
|
||||
private pollRepo: AssignmentPollRepositoryService
|
||||
) {
|
||||
super(constants);
|
||||
super(constants, translate, pollKeyVerbose, parsePollNumber);
|
||||
config
|
||||
.get<AssignmentPollPercentBase>('assignment_poll_default_100_percent_base')
|
||||
.subscribe(base => (this.defaultPercentBase = base));
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { Component, ViewEncapsulation } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
@ -14,8 +14,8 @@ import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
||||
import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
||||
import { MotionPollDialogService } from 'app/site/motions/services/motion-poll-dialog.service';
|
||||
import { MotionPollService } from 'app/site/motions/services/motion-poll.service';
|
||||
import { BasePollDetailComponent } from 'app/site/polls/components/base-poll-detail.component';
|
||||
import { PollService } from 'app/site/polls/services/poll.service';
|
||||
|
||||
@Component({
|
||||
selector: 'os-motion-poll-detail',
|
||||
@ -23,7 +23,7 @@ import { PollService } from 'app/site/polls/services/poll.service';
|
||||
styleUrls: ['./motion-poll-detail.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class MotionPollDetailComponent extends BasePollDetailComponent<ViewMotionPoll> implements OnInit {
|
||||
export class MotionPollDetailComponent extends BasePollDetailComponent<ViewMotionPoll, MotionPollService> {
|
||||
public motion: ViewMotion;
|
||||
public columnDefinition: PblColumnDefinition[] = [
|
||||
{
|
||||
@ -49,7 +49,7 @@ export class MotionPollDetailComponent extends BasePollDetailComponent<ViewMotio
|
||||
groupRepo: GroupRepositoryService,
|
||||
prompt: PromptService,
|
||||
pollDialog: MotionPollDialogService,
|
||||
pollService: PollService,
|
||||
pollService: MotionPollService,
|
||||
votesRepo: MotionVoteRepositoryService,
|
||||
private operator: OperatorService,
|
||||
private router: Router
|
||||
|
@ -1,36 +0,0 @@
|
||||
<os-head-bar>
|
||||
<div class="title-slot">Motions poll list</div>
|
||||
|
||||
<!-- Menu -->
|
||||
<div class="menu-slot">
|
||||
<button type="button" mat-icon-button [matMenuTriggerFor]="pollMenu"><mat-icon>more_vert</mat-icon></button>
|
||||
</div>
|
||||
</os-head-bar>
|
||||
|
||||
<os-list-view-table
|
||||
[listObservableProvider]="repo"
|
||||
[vScrollFixed]="64"
|
||||
[columns]="tableColumnDefinition"
|
||||
[listStorageKey]="'motion-polls'"
|
||||
>
|
||||
<div *pblNgridCellDef="'title'; row as poll; rowContext as context" class="cell-slot fill">
|
||||
<a
|
||||
class="detail-link"
|
||||
(click)="saveScrollIndex('motion-polls', rowContext.identity)"
|
||||
[routerLink]="poll.id"
|
||||
*ngIf="!isMultiSelect"
|
||||
></a>
|
||||
<span>{{ poll.title }}</span>
|
||||
</div>
|
||||
<div *pblNgridCellDef="'state'; row as poll; rowContext as context" class="cell-slot fill">
|
||||
<span>{{ poll.stateVerbose }}</span>
|
||||
</div>
|
||||
</os-list-view-table>
|
||||
|
||||
<mat-menu #pollMenu="matMenu">
|
||||
<!-- Settings -->
|
||||
<button mat-menu-item *osPerms="'core.can_manage_config'" routerLink="/settings/polls">
|
||||
<mat-icon>settings</mat-icon>
|
||||
<span translate>Settings</span>
|
||||
</button>
|
||||
</mat-menu>
|
@ -1,27 +0,0 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { E2EImportsModule } from 'e2e-imports.module';
|
||||
|
||||
import { MotionPollListComponent } from './motion-poll-list.component';
|
||||
|
||||
describe('MotionPollListComponent', () => {
|
||||
let component: MotionPollListComponent;
|
||||
let fixture: ComponentFixture<MotionPollListComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [E2EImportsModule],
|
||||
declarations: [MotionPollListComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MotionPollListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,45 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { PblColumnDefinition } from '@pebula/ngrid';
|
||||
|
||||
import { StorageService } from 'app/core/core-services/storage.service';
|
||||
import { MotionPollRepositoryService } from 'app/core/repositories/motions/motion-poll-repository.service';
|
||||
import { BaseListViewComponent } from 'app/site/base/base-list-view';
|
||||
import { ViewMotionPoll } from 'app/site/motions/models/view-motion-poll';
|
||||
|
||||
@Component({
|
||||
selector: 'os-motion-poll-list',
|
||||
templateUrl: './motion-poll-list.component.html',
|
||||
styleUrls: ['./motion-poll-list.component.scss']
|
||||
})
|
||||
export class MotionPollListComponent extends BaseListViewComponent<ViewMotionPoll> implements OnInit {
|
||||
public tableColumnDefinition: PblColumnDefinition[] = [
|
||||
{
|
||||
prop: 'title',
|
||||
width: 'auto'
|
||||
},
|
||||
{
|
||||
prop: 'state',
|
||||
width: 'auto'
|
||||
}
|
||||
];
|
||||
|
||||
public polls: ViewMotionPoll[] = [];
|
||||
|
||||
public constructor(
|
||||
title: Title,
|
||||
protected translate: TranslateService,
|
||||
matSnackbar: MatSnackBar,
|
||||
storage: StorageService,
|
||||
public repo: MotionPollRepositoryService
|
||||
) {
|
||||
super(title, translate, matSnackbar, storage);
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.subscriptions.push(this.repo.getViewModelListObservable().subscribe(polls => (this.polls = polls)));
|
||||
}
|
||||
}
|
@ -2,10 +2,8 @@ import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { MotionPollDetailComponent } from './motion-poll-detail/motion-poll-detail.component';
|
||||
import { MotionPollListComponent } from './motion-poll-list/motion-poll-list.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', component: MotionPollListComponent, pathMatch: 'full' },
|
||||
{ path: 'new', component: MotionPollDetailComponent },
|
||||
{ path: ':id', component: MotionPollDetailComponent }
|
||||
];
|
||||
|
@ -4,7 +4,6 @@ import { NgModule } from '@angular/core';
|
||||
import { SharedModule } from 'app/shared/shared.module';
|
||||
import { PollsModule } from 'app/site/polls/polls.module';
|
||||
import { MotionPollDetailComponent } from './motion-poll-detail/motion-poll-detail.component';
|
||||
import { MotionPollListComponent } from './motion-poll-list/motion-poll-list.component';
|
||||
import { MotionPollRoutingModule } from './motion-poll-routing.module';
|
||||
import { MotionPollVoteComponent } from './motion-poll-vote/motion-poll-vote.component';
|
||||
import { MotionPollComponent } from './motion-poll/motion-poll.component';
|
||||
@ -12,6 +11,6 @@ import { MotionPollComponent } from './motion-poll/motion-poll.component';
|
||||
@NgModule({
|
||||
imports: [CommonModule, SharedModule, MotionPollRoutingModule, PollsModule],
|
||||
exports: [MotionPollComponent],
|
||||
declarations: [MotionPollComponent, MotionPollDetailComponent, MotionPollListComponent, MotionPollVoteComponent]
|
||||
declarations: [MotionPollComponent, MotionPollDetailComponent, MotionPollVoteComponent]
|
||||
})
|
||||
export class MotionPollModule {}
|
||||
|
@ -2,15 +2,25 @@
|
||||
<!-- Poll Infos -->
|
||||
<div class="poll-title-wrapper">
|
||||
<!-- Title Area -->
|
||||
<div class="poll-title-area spacer-bottom-20">
|
||||
<div class="poll-title-area">
|
||||
<!-- Title -->
|
||||
<span class="poll-title">
|
||||
<a [routerLink]="pollLink">
|
||||
{{ poll.title | translate }}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="italic">
|
||||
<!-- Dot Menu -->
|
||||
<span class="poll-actions" *osPerms="'motions.can_manage_polls'">
|
||||
<button mat-icon-button [matMenuTriggerFor]="pollDetailMenu">
|
||||
<mat-icon class="small-icon">more_horiz</mat-icon>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Subtitle -->
|
||||
<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>
|
||||
@ -29,15 +39,6 @@
|
||||
{{ poll.stateVerbose | translate }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dot Menu -->
|
||||
<span class="poll-actions" *osPerms="'motions.can_manage_polls'">
|
||||
<button mat-icon-button [matMenuTriggerFor]="pollDetailMenu">
|
||||
<mat-icon class="small-icon">more_horiz</mat-icon>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Change state button -->
|
||||
<div *osPerms="'motions.can_manage_polls'; and: !hideChangeState">
|
||||
|
@ -7,6 +7,8 @@ import { MotionPollRepositoryService } from 'app/core/repositories/motions/motio
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { MotionPoll, MotionPollMethod } from 'app/shared/models/motions/motion-poll';
|
||||
import { MajorityMethod, PercentBase } from 'app/shared/models/poll/base-poll';
|
||||
import { ParsePollNumberPipe } from 'app/shared/pipes/parse-poll-number.pipe';
|
||||
import { PollKeyVerbosePipe } from 'app/shared/pipes/poll-key-verbose.pipe';
|
||||
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';
|
||||
@ -43,10 +45,12 @@ export class MotionPollService extends PollService {
|
||||
public constructor(
|
||||
config: ConfigService,
|
||||
constants: ConstantsService,
|
||||
private translate: TranslateService,
|
||||
pollKeyVerbose: PollKeyVerbosePipe,
|
||||
parsePollNumber: ParsePollNumberPipe,
|
||||
protected translate: TranslateService,
|
||||
private pollRepo: MotionPollRepositoryService
|
||||
) {
|
||||
super(constants);
|
||||
super(constants, translate, pollKeyVerbose, parsePollNumber);
|
||||
config
|
||||
.get<PercentBase>('motion_poll_default_100_percent_base')
|
||||
.subscribe(base => (this.defaultPercentBase = base));
|
||||
|
@ -27,7 +27,8 @@ export interface BaseVoteData {
|
||||
user?: ViewUser;
|
||||
}
|
||||
|
||||
export abstract class BasePollDetailComponent<V extends ViewBasePoll> extends BaseViewComponent implements OnInit {
|
||||
export abstract class BasePollDetailComponent<V extends ViewBasePoll, S extends PollService> extends BaseViewComponent
|
||||
implements OnInit {
|
||||
/**
|
||||
* All the groups of users.
|
||||
*/
|
||||
@ -100,7 +101,7 @@ export abstract class BasePollDetailComponent<V extends ViewBasePoll> extends Ba
|
||||
protected groupRepo: GroupRepositoryService,
|
||||
protected promptService: PromptService,
|
||||
protected pollDialog: BasePollDialogService<V>,
|
||||
protected pollService: PollService,
|
||||
protected pollService: S,
|
||||
protected votesRepo: BaseRepository<ViewBaseVote, BaseVote, object>
|
||||
) {
|
||||
super(title, translate, matSnackbar);
|
||||
|
@ -10,20 +10,35 @@
|
||||
[filterProps]="filterProps"
|
||||
[filterService]="filterService"
|
||||
>
|
||||
<div *pblNgridCellDef="'title'; row as poll; rowContext as context;" class="cell-slot fill">
|
||||
<!-- Poll Title -->
|
||||
<div *pblNgridCellDef="'title'; row as poll; rowContext as context" class="cell-slot fill">
|
||||
<a class="detail-link" [routerLink]="poll.parentLink" *ngIf="!isMultiSelect"></a>
|
||||
<span>{{ poll.title }}</span>
|
||||
</div>
|
||||
<div *pblNgridCellDef="'classType'; row as poll;" class="cell-slot fill">
|
||||
|
||||
<!-- Motion Or Assigmnent Title Title -->
|
||||
<div *pblNgridCellDef="'classType'; row as poll" class="cell-slot fill">
|
||||
<a class="detail-link" [routerLink]="poll.parentLink" *ngIf="!isMultiSelect"></a>
|
||||
<span>{{ poll.getContentObject().getListTitle() }}</span>
|
||||
</div>
|
||||
<div *pblNgridCellDef="'state'; row as poll;" class="cell-slot fill">
|
||||
|
||||
<!-- State -->
|
||||
<div *pblNgridCellDef="'state'; row as poll" class="cell-slot fill">
|
||||
<a class="detail-link" [routerLink]="poll.parentLink" *ngIf="!isMultiSelect"></a>
|
||||
<span>{{ poll.stateVerbose | translate }}</span>
|
||||
</div>
|
||||
<div *pblNgridCellDef="'votability'; row as poll;" class="cell-slot fill">
|
||||
<mat-icon *ngIf="poll.user_has_voted_valid" color="accent" matTooltip="{{ 'You have already voted.' | translate }}">check_circle</mat-icon>
|
||||
<mat-icon *ngIf="!poll.user_has_voted_valid && poll.canBeVotedFor" color="warn" matTooltip="{{ 'Voting is currently in progress.' | translate }}">warning</mat-icon>
|
||||
|
||||
<!-- Voted Indicator -->
|
||||
<div *pblNgridCellDef="'votability'; row as poll" class="cell-slot fill">
|
||||
<mat-icon *ngIf="poll.user_has_voted" color="accent" matTooltip="{{ 'You have already voted.' | translate }}">
|
||||
check_circle
|
||||
</mat-icon>
|
||||
<mat-icon
|
||||
*ngIf="!poll.user_has_voted && poll.canBeVotedFor"
|
||||
color="warn"
|
||||
matTooltip="{{ 'Voting is currently in progress.' | translate }}"
|
||||
>
|
||||
warning
|
||||
</mat-icon>
|
||||
</div>
|
||||
</os-list-view-table>
|
||||
|
@ -29,7 +29,7 @@ export class PollListComponent extends BaseListViewComponent<ViewBasePoll> {
|
||||
},
|
||||
{
|
||||
prop: 'state',
|
||||
width: '70px'
|
||||
width: 'auto'
|
||||
},
|
||||
{
|
||||
prop: 'votability',
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { _ } from 'app/core/translate/translation-marker';
|
||||
import { ChartData, ChartDate } from 'app/shared/components/charts/charts.component';
|
||||
import { AssignmentPollMethod } from 'app/shared/models/assignments/assignment-poll';
|
||||
@ -11,6 +13,8 @@ import {
|
||||
PollType,
|
||||
VOTE_UNDOCUMENTED
|
||||
} from 'app/shared/models/poll/base-poll';
|
||||
import { ParsePollNumberPipe } from 'app/shared/pipes/parse-poll-number.pipe';
|
||||
import { PollKeyVerbosePipe } from 'app/shared/pipes/poll-key-verbose.pipe';
|
||||
import { AssignmentPollMethodVerbose } from 'app/site/assignments/models/view-assignment-poll';
|
||||
import {
|
||||
MajorityMethodVerbose,
|
||||
@ -21,6 +25,7 @@ import {
|
||||
} from 'app/site/polls/models/view-base-poll';
|
||||
import { ConstantsService } from '../../../core/core-services/constants.service';
|
||||
|
||||
const PERCENT_DECIMAL_PLACES = 3;
|
||||
/**
|
||||
* The possible keys of a poll object that represent numbers.
|
||||
* TODO Should be 'key of MotionPoll|AssinmentPoll if type of key is number'
|
||||
@ -178,12 +183,32 @@ export abstract class PollService {
|
||||
*/
|
||||
public pollValues: CalculablePollKey[] = ['yes', 'no', 'abstain', 'votesvalid', 'votesinvalid', 'votescast'];
|
||||
|
||||
public constructor(constants: ConstantsService) {
|
||||
public constructor(
|
||||
constants: ConstantsService,
|
||||
protected translate: TranslateService,
|
||||
private pollKeyVerbose: PollKeyVerbosePipe,
|
||||
private parsePollNumber: ParsePollNumberPipe
|
||||
) {
|
||||
constants
|
||||
.get<OpenSlidesSettings>('Settings')
|
||||
.subscribe(settings => (this.isElectronicVotingEnabled = settings.ENABLE_ELECTRONIC_VOTING));
|
||||
}
|
||||
|
||||
/**
|
||||
* return the total number of votes depending on the selected percent base
|
||||
*/
|
||||
public abstract getPercentBase(poll: PollData): number;
|
||||
|
||||
public getVoteValueInPercent(value: number, poll: PollData): string | null {
|
||||
const totalByBase = this.getPercentBase(poll);
|
||||
if (totalByBase && totalByBase > 0) {
|
||||
const percentNumber = (value / totalByBase) * 100;
|
||||
const result = percentNumber % 1 === 0 ? percentNumber : percentNumber.toFixed(PERCENT_DECIMAL_PLACES);
|
||||
return `${result} %`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the default poll data to the object. To be extended in subclasses
|
||||
* @param poll the poll/object to fill
|
||||
@ -268,8 +293,24 @@ export abstract class PollService {
|
||||
}
|
||||
|
||||
public generateChartData(poll: PollData | ViewBasePoll): ChartData {
|
||||
const fields = this.getPollDataFields(poll);
|
||||
|
||||
const data: ChartData = fields.map(key => {
|
||||
return {
|
||||
data: this.getResultFromPoll(poll, key),
|
||||
label: key.toUpperCase(),
|
||||
backgroundColor: PollColor[key],
|
||||
hoverBackgroundColor: PollColor[key]
|
||||
} as ChartDate;
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private getPollDataFields(poll: PollData | ViewBasePoll): CalculablePollKey[] {
|
||||
let fields: CalculablePollKey[];
|
||||
let isAssignment: boolean;
|
||||
|
||||
if (poll instanceof ViewBasePoll) {
|
||||
isAssignment = poll.pollClassType === 'assignment';
|
||||
} else {
|
||||
@ -294,16 +335,7 @@ export abstract class PollService {
|
||||
}
|
||||
}
|
||||
|
||||
const data: ChartData = fields.map(key => {
|
||||
return {
|
||||
data: this.getResultFromPoll(poll, key),
|
||||
label: key.toUpperCase(),
|
||||
backgroundColor: PollColor[key],
|
||||
hoverBackgroundColor: PollColor[key]
|
||||
} as ChartDate;
|
||||
});
|
||||
|
||||
return data;
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -314,7 +346,17 @@ export abstract class PollService {
|
||||
}
|
||||
|
||||
public getChartLabels(poll: PollData): string[] {
|
||||
return poll.options.map(candidate => candidate.user.short_name);
|
||||
const fields = this.getPollDataFields(poll);
|
||||
return poll.options.map(option => {
|
||||
const votingResults = fields.map(field => {
|
||||
const votingKey = this.translate.instant(this.pollKeyVerbose.transform(field));
|
||||
const resultValue = this.parsePollNumber.transform(option[field]);
|
||||
const resultInPercent = this.getVoteValueInPercent(option[field], poll);
|
||||
return `${votingKey} ${resultValue} (${resultInPercent})`;
|
||||
});
|
||||
|
||||
return `${option.user.short_name} · ${votingResults.join(' · ')}`;
|
||||
});
|
||||
}
|
||||
|
||||
public isVoteDocumented(vote: number): boolean {
|
||||
|
@ -864,6 +864,32 @@ button.mat-menu-item.selected {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix to enable multi line mat hints. See:
|
||||
* https://github.com/angular/components/issues/5227
|
||||
*/
|
||||
.mat-form-field {
|
||||
.mat-form-field-wrapper {
|
||||
padding-bottom: 0;
|
||||
|
||||
.mat-form-field-underline {
|
||||
position: initial !important;
|
||||
display: block;
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.mat-form-field-subscript-wrapper,
|
||||
.mat-form-field-ripple {
|
||||
position: initial !important;
|
||||
display: table;
|
||||
}
|
||||
|
||||
.mat-form-field-subscript-wrapper {
|
||||
min-height: calc(1em + 1px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use to disable events on (i.e) matMenuTriggerFor
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user