Rework assignment poll slide
Reworked assignment poll slide and refactored the assignment poll detail table into an own component
This commit is contained in:
parent
b873dc156b
commit
072ec937a1
@ -0,0 +1,42 @@
|
||||
<div *ngIf="poll">
|
||||
<table class="assignment-result-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="voting-option" translate>Candidates</th>
|
||||
<th class="result yes">
|
||||
<span *ngIf="!isMethodY" translate>
|
||||
Yes
|
||||
</span>
|
||||
<span *ngIf="isMethodY" translate>
|
||||
Votes
|
||||
</span>
|
||||
</th>
|
||||
<th class="result no" translate *ngIf="!isMethodY">No</th>
|
||||
<th class="result abstain" translate *ngIf="isMethodYNA">Abstain</th>
|
||||
</tr>
|
||||
<tr *ngFor="let row of tableData" [class]="row.class">
|
||||
<td class="voting-option">
|
||||
<div>
|
||||
<span>
|
||||
{{ row.votingOption | pollKeyVerbose | translate }}
|
||||
</span>
|
||||
<span class="user-subtitle" *ngIf="row.votingOptionSubtitle">
|
||||
<br />
|
||||
{{ row.votingOptionSubtitle }}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="result" *ngFor="let vote of row.value">
|
||||
<div class="single-result" [ngClass]="getVoteClass(vote)" *ngIf="vote && voteFitsMethod(vote)">
|
||||
<span>
|
||||
<span *ngIf="vote.showPercent">
|
||||
{{ vote.amount | pollPercentBase: poll }}
|
||||
</span>
|
||||
{{ vote.amount | parsePollNumber }}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
@ -0,0 +1,49 @@
|
||||
@import '~assets/styles/poll-styles-common.scss';
|
||||
|
||||
.assignment-result-table {
|
||||
margin-top: 2em;
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
border-collapse: collapse;
|
||||
|
||||
th {
|
||||
font-weight: initial;
|
||||
}
|
||||
|
||||
tr {
|
||||
height: 48px;
|
||||
|
||||
td:first-child {
|
||||
padding-right: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
tr.sums {
|
||||
border-bottom: none;
|
||||
td {
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.result {
|
||||
text-align: right;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.voting-option {
|
||||
min-width: 200px;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.user + .sums {
|
||||
td {
|
||||
padding-top: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.single-result {
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { E2EImportsModule } from 'e2e-imports.module';
|
||||
|
||||
import { AssignmentPollDetailContentComponent } from './assignment-poll-detail-content.component';
|
||||
|
||||
describe('AssignmentPollDetailContentComponent', () => {
|
||||
let component: AssignmentPollDetailContentComponent;
|
||||
let fixture: ComponentFixture<AssignmentPollDetailContentComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [E2EImportsModule]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AssignmentPollDetailContentComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,55 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
import { AssignmentPollMethod } from 'app/shared/models/assignments/assignment-poll';
|
||||
import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll';
|
||||
import { AssignmentPollService } from 'app/site/assignments/services/assignment-poll.service';
|
||||
import { PollData, PollTableData, VotingResult } from 'app/site/polls/services/poll.service';
|
||||
|
||||
@Component({
|
||||
selector: 'os-assignment-poll-detail-content',
|
||||
templateUrl: './assignment-poll-detail-content.component.html',
|
||||
styleUrls: ['./assignment-poll-detail-content.component.scss']
|
||||
})
|
||||
export class AssignmentPollDetailContentComponent {
|
||||
@Input()
|
||||
public poll: ViewAssignmentPoll | PollData;
|
||||
|
||||
public constructor(private pollService: AssignmentPollService) {}
|
||||
|
||||
private get method(): string {
|
||||
return this.poll.pollmethod;
|
||||
}
|
||||
|
||||
public get isMethodY(): boolean {
|
||||
return this.method === AssignmentPollMethod.Votes;
|
||||
}
|
||||
|
||||
public get isMethodYN(): boolean {
|
||||
return this.method === AssignmentPollMethod.YN;
|
||||
}
|
||||
|
||||
public get isMethodYNA(): boolean {
|
||||
return this.method === AssignmentPollMethod.YNA;
|
||||
}
|
||||
|
||||
public get tableData(): PollTableData[] {
|
||||
return this.pollService.generateTableData(this.poll);
|
||||
}
|
||||
|
||||
public getVoteClass(votingResult: VotingResult): string {
|
||||
return votingResult.vote;
|
||||
}
|
||||
|
||||
public voteFitsMethod(result: VotingResult): boolean {
|
||||
if (this.isMethodY) {
|
||||
if (result.vote === 'abstain' || result.vote === 'no') {
|
||||
return false;
|
||||
}
|
||||
} else if (this.isMethodYN) {
|
||||
if (result.vote === 'abstain') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -123,6 +123,7 @@ import { PollKeyVerbosePipe } from './pipes/poll-key-verbose.pipe';
|
||||
import { PollPercentBasePipe } from './pipes/poll-percent-base.pipe';
|
||||
import { VotingPrivacyWarningComponent } from './components/voting-privacy-warning/voting-privacy-warning.component';
|
||||
import { MotionPollDetailContentComponent } from './components/motion-poll-detail-content/motion-poll-detail-content.component';
|
||||
import { AssignmentPollDetailContentComponent } from './components/assignment-poll-detail-content/assignment-poll-detail-content.component';
|
||||
|
||||
/**
|
||||
* Share Module for all "dumb" components and pipes.
|
||||
@ -288,7 +289,8 @@ import { MotionPollDetailContentComponent } from './components/motion-poll-detai
|
||||
PollKeyVerbosePipe,
|
||||
PollPercentBasePipe,
|
||||
VotingPrivacyWarningComponent,
|
||||
MotionPollDetailContentComponent
|
||||
MotionPollDetailContentComponent,
|
||||
AssignmentPollDetailContentComponent
|
||||
],
|
||||
declarations: [
|
||||
PermsDirective,
|
||||
@ -347,7 +349,8 @@ import { MotionPollDetailContentComponent } from './components/motion-poll-detai
|
||||
PollKeyVerbosePipe,
|
||||
PollPercentBasePipe,
|
||||
VotingPrivacyWarningComponent,
|
||||
MotionPollDetailContentComponent
|
||||
MotionPollDetailContentComponent,
|
||||
AssignmentPollDetailContentComponent
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
|
@ -28,54 +28,9 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="assignment-result-wrapper" *ngIf="poll.stateHasVotes">
|
||||
<div class="assignment-result-wrapper" *ngIf="poll && poll.stateHasVotes">
|
||||
<!-- Result Table -->
|
||||
<div>
|
||||
<table class="assignment-result-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="voting-option" translate>Candidates</th>
|
||||
<th class="result yes">
|
||||
<span *ngIf="!poll.isMethodY" translate>
|
||||
Yes
|
||||
</span>
|
||||
<span *ngIf="poll.isMethodY" translate>
|
||||
Votes
|
||||
</span>
|
||||
</th>
|
||||
<th class="result no" translate *ngIf="!poll.isMethodY">No</th>
|
||||
<th class="result abstain" translate *ngIf="poll.isMethodYNA">Abstain</th>
|
||||
</tr>
|
||||
<tr *ngFor="let row of getTableData()" [class]="row.class">
|
||||
<td class="voting-option">
|
||||
<div>
|
||||
<span>
|
||||
{{ row.votingOption | pollKeyVerbose | translate }}
|
||||
</span>
|
||||
<span class="user-subtitle" *ngIf="row.votingOptionSubtitle">
|
||||
<br />
|
||||
{{ row.votingOptionSubtitle }}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="result" *ngFor="let vote of row.value">
|
||||
<div
|
||||
class="single-result"
|
||||
[ngClass]="getVoteClass(vote)"
|
||||
*ngIf="vote && voteFitsMethod(vote)"
|
||||
>
|
||||
<span>
|
||||
<span *ngIf="vote.showPercent">
|
||||
{{ vote.amount | pollPercentBase: poll }}
|
||||
</span>
|
||||
{{ vote.amount | parsePollNumber }}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<os-assignment-poll-detail-content [poll]="poll"></os-assignment-poll-detail-content>
|
||||
|
||||
<!-- Result Chart -->
|
||||
<div class="chart-wrapper">
|
||||
|
@ -1,55 +1,4 @@
|
||||
@import '~assets/styles/poll-colors.scss';
|
||||
@import '~assets/styles/poll-styles-common.scss';
|
||||
|
||||
.assignment-result-wrapper {
|
||||
.assignment-result-table {
|
||||
margin-top: 2em;
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
border-collapse: collapse;
|
||||
|
||||
th {
|
||||
font-weight: initial;
|
||||
}
|
||||
|
||||
tr {
|
||||
height: 48px;
|
||||
|
||||
td:first-child {
|
||||
padding-right: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
tr.sums {
|
||||
border-bottom: none;
|
||||
td {
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.result {
|
||||
text-align: right;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.voting-option {
|
||||
min-width: 200px;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.user + .sums {
|
||||
td {
|
||||
padding-top: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.single-result {
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-wrapper {
|
||||
margin-top: 2em;
|
||||
.pie-chart {
|
||||
|
@ -13,7 +13,6 @@ import { GroupRepositoryService } from 'app/core/repositories/users/group-reposi
|
||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||
import { VoteValue } from 'app/shared/models/poll/base-vote';
|
||||
import { BasePollDetailComponent } from 'app/site/polls/components/base-poll-detail.component';
|
||||
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';
|
||||
@ -121,27 +120,6 @@ export class AssignmentPollDetailComponent extends BasePollDetailComponent<ViewA
|
||||
return this.operator.hasPerms('assignments.can_manage');
|
||||
}
|
||||
|
||||
public getVoteClass(votingResult: VotingResult): string {
|
||||
return votingResult.vote;
|
||||
}
|
||||
|
||||
public voteFitsMethod(result: VotingResult): boolean {
|
||||
if (this.poll.isMethodY) {
|
||||
if (result.vote === 'abstain' || result.vote === 'no') {
|
||||
return false;
|
||||
}
|
||||
} else if (this.poll.isMethodYN) {
|
||||
if (result.vote === 'abstain') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public getTableData(): PollTableData[] {
|
||||
return this.pollService.generateTableData(this.poll);
|
||||
}
|
||||
|
||||
protected onDeleted(): void {
|
||||
this.router.navigate(['assignments', this.poll.assignment_id]);
|
||||
}
|
||||
|
@ -13,7 +13,14 @@ import {
|
||||
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 {
|
||||
PollData,
|
||||
PollDataOption,
|
||||
PollService,
|
||||
PollTableData,
|
||||
VotingResult
|
||||
} from 'app/site/polls/services/poll.service';
|
||||
import { ViewAssignmentOption } from '../models/view-assignment-option';
|
||||
import { ViewAssignmentPoll } from '../models/view-assignment-poll';
|
||||
|
||||
@Injectable({
|
||||
@ -80,7 +87,7 @@ export class AssignmentPollService extends PollService {
|
||||
return poll;
|
||||
}
|
||||
|
||||
private getGlobalVoteKeys(poll: ViewAssignmentPoll): VotingResult[] {
|
||||
private getGlobalVoteKeys(poll: ViewAssignmentPoll | PollData): VotingResult[] {
|
||||
return [
|
||||
{
|
||||
vote: 'amount_global_no',
|
||||
@ -95,30 +102,45 @@ export class AssignmentPollService extends PollService {
|
||||
];
|
||||
}
|
||||
|
||||
public generateTableData(poll: ViewAssignmentPoll): PollTableData[] {
|
||||
public generateTableData(poll: ViewAssignmentPoll | PollData): PollTableData[] {
|
||||
console.log('poll: ', poll);
|
||||
|
||||
const tableData: PollTableData[] = poll.options
|
||||
.sort((a, b) => {
|
||||
if (this.sortByVote) {
|
||||
return b.yes - a.yes;
|
||||
} else {
|
||||
return b.weight - a.weight;
|
||||
// PollData does not have weight, we need to rely on the order of things.
|
||||
if (a.weight && b.weight) {
|
||||
return b.weight - a.weight;
|
||||
}
|
||||
}
|
||||
})
|
||||
.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)
|
||||
)
|
||||
}));
|
||||
.map((candidate: ViewAssignmentOption) => {
|
||||
const pollTableEntry: PollTableData = {
|
||||
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)
|
||||
)
|
||||
};
|
||||
|
||||
// Since pollData does not have any subtitle option
|
||||
if (candidate instanceof ViewAssignmentOption) {
|
||||
pollTableEntry.votingOption = candidate.user.short_name;
|
||||
pollTableEntry.votingOptionSubtitle = candidate.user.getLevelAndNumber();
|
||||
} else {
|
||||
pollTableEntry.votingOption = (candidate as PollDataOption).user.short_name;
|
||||
}
|
||||
|
||||
return pollTableEntry;
|
||||
});
|
||||
tableData.push(...this.formatVotingResultToTableData(this.getGlobalVoteKeys(poll), poll));
|
||||
tableData.push(...this.formatVotingResultToTableData(super.getSumTableKeys(poll), poll));
|
||||
return tableData;
|
||||
|
@ -105,17 +105,22 @@ export interface PollData {
|
||||
pollmethod: string;
|
||||
type: string;
|
||||
onehundred_percent_base: string;
|
||||
options: {
|
||||
user?: {
|
||||
short_name: string;
|
||||
};
|
||||
yes?: number;
|
||||
no?: number;
|
||||
abstain?: number;
|
||||
}[];
|
||||
options: PollDataOption[];
|
||||
votesvalid: number;
|
||||
votesinvalid: number;
|
||||
votescast: number;
|
||||
amount_global_no?: number;
|
||||
amount_global_abstain?: number;
|
||||
}
|
||||
|
||||
export interface PollDataOption {
|
||||
user?: {
|
||||
short_name?: string;
|
||||
};
|
||||
yes?: number;
|
||||
no?: number;
|
||||
abstain?: number;
|
||||
weight?: number;
|
||||
}
|
||||
|
||||
interface OpenSlidesSettings {
|
||||
@ -126,7 +131,7 @@ interface OpenSlidesSettings {
|
||||
* Interface describes the possible data for the result-table.
|
||||
*/
|
||||
export interface PollTableData {
|
||||
votingOption: string;
|
||||
votingOption?: string;
|
||||
votingOptionSubtitle?: string;
|
||||
class?: string;
|
||||
value: VotingResult[];
|
||||
|
@ -3,11 +3,7 @@
|
||||
<h1 class="assignment-title">{{ data.data.assignment.title }}</h1>
|
||||
<h2 class="poll-title">{{ data.data.poll.title }}</h2>
|
||||
</div>
|
||||
<div class="charts-wrapper" *ngIf="data.data.poll.state === PollState.Published">
|
||||
<os-charts
|
||||
[labels]="pollService.getChartLabels(data.data.poll)"
|
||||
[data]="chartDataSubject | async"
|
||||
[hasPadding]="false"
|
||||
></os-charts>
|
||||
<div *ngIf="data.data.poll.state === PollState.Published">
|
||||
<os-assignment-poll-detail-content [poll]="data.data.poll"></os-assignment-poll-detail-content>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
@ -5,7 +5,3 @@
|
||||
.slidetitle {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.charts-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ async def assignment_poll_slide(
|
||||
options = get_models(all_data, "assignments/assignment-option", poll["options_id"])
|
||||
for option in sorted(options, key=lambda option: option["weight"]):
|
||||
option_data: Dict[str, Any] = {
|
||||
"user": {"full_name": await get_user_name(all_data, option["user_id"])}
|
||||
"user": {"short_name": await get_user_name(all_data, option["user_id"])}
|
||||
}
|
||||
if poll["state"] == AssignmentPoll.STATE_PUBLISHED:
|
||||
option_data["yes"] = float(option["yes"])
|
||||
|
@ -16,3 +16,4 @@ roman>=2.0,<3.2
|
||||
setuptools>=29.0,<42.0
|
||||
typing_extensions>=3.6.6,<3.8
|
||||
websockets>=8.0,<9.0
|
||||
twisted>=19.0,<20.0
|
Loading…
Reference in New Issue
Block a user