Cleanups and enhancements
Cleans up and reviews some methods
This commit is contained in:
parent
464fb89b53
commit
054f76a5d4
@ -10,6 +10,7 @@ import { DataSendService } from 'app/core/core-services/data-send.service';
|
|||||||
import { DataStoreService } from '../../core-services/data-store.service';
|
import { DataStoreService } from '../../core-services/data-store.service';
|
||||||
import { HttpService } from 'app/core/core-services/http.service';
|
import { HttpService } from 'app/core/core-services/http.service';
|
||||||
import { Item } from 'app/shared/models/agenda/item';
|
import { Item } from 'app/shared/models/agenda/item';
|
||||||
|
import { Poll } from 'app/shared/models/assignments/poll';
|
||||||
import { Tag } from 'app/shared/models/core/tag';
|
import { Tag } from 'app/shared/models/core/tag';
|
||||||
import { User } from 'app/shared/models/users/user';
|
import { User } from 'app/shared/models/users/user';
|
||||||
import { ViewAssignment } from 'app/site/assignments/models/view-assignment';
|
import { ViewAssignment } from 'app/site/assignments/models/view-assignment';
|
||||||
@ -17,7 +18,6 @@ import { ViewItem } from 'app/site/agenda/models/view-item';
|
|||||||
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
|
||||||
import { ViewTag } from 'app/site/tags/models/view-tag';
|
import { ViewTag } from 'app/site/tags/models/view-tag';
|
||||||
import { ViewUser } from 'app/site/users/models/view-user';
|
import { ViewUser } from 'app/site/users/models/view-user';
|
||||||
import { Poll } from 'app/shared/models/assignments/poll';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository Service for Assignments.
|
* Repository Service for Assignments.
|
||||||
@ -28,14 +28,22 @@ import { Poll } from 'app/shared/models/assignments/poll';
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class AssignmentRepositoryService extends BaseAgendaContentObjectRepository<ViewAssignment, Assignment> {
|
export class AssignmentRepositoryService extends BaseAgendaContentObjectRepository<ViewAssignment, Assignment> {
|
||||||
|
private readonly restPath = '/rest/assignments/assignment/';
|
||||||
|
private readonly restPollPath = '/rest/assignments/poll/';
|
||||||
|
private readonly candidatureOtherPath = '/candidature_other/';
|
||||||
|
private readonly candidatureSelfPath = '/candidature_self/';
|
||||||
|
private readonly createPollPath = '/create_poll/';
|
||||||
|
private readonly markElectedPath = '/mark_elected/';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for the Assignment Repository.
|
* Constructor for the Assignment Repository.
|
||||||
*
|
*
|
||||||
* @param DS The DataStore
|
* @param DS DataStore access
|
||||||
* @param mapperService Maps collection strings to classes
|
* @param dataSend Sending data
|
||||||
* @param viewModelStoreService
|
* @param mapperService Map models to object
|
||||||
* @param translate
|
* @param viewModelStoreService Access view models
|
||||||
* @param httpService
|
* @param translate Translate string
|
||||||
|
* @param httpService make HTTP Requests
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
DS: DataStoreService,
|
DS: DataStoreService,
|
||||||
@ -76,55 +84,42 @@ export class AssignmentRepositoryService extends BaseAgendaContentObjectReposito
|
|||||||
* Adds another user as a candidate
|
* Adds another user as a candidate
|
||||||
*
|
*
|
||||||
* @param userId User id of a candidate
|
* @param userId User id of a candidate
|
||||||
* @param assignment
|
* @param assignment The assignment to add the candidate to
|
||||||
*/
|
*/
|
||||||
public async addCandidate(userId: number, assignment: ViewAssignment): Promise<void> {
|
public async changeCandidate(userId: number, assignment: ViewAssignment): Promise<void> {
|
||||||
const restPath = `/rest/assignments/assignment/${assignment.id}/candidature_other/`;
|
|
||||||
const data = { user: userId };
|
const data = { user: userId };
|
||||||
await this.httpService.post(restPath, data);
|
if (assignment.candidates.some(candidate => candidate.id === userId)) {
|
||||||
}
|
await this.httpService.delete(this.restPath + assignment.id + this.candidatureOtherPath, data);
|
||||||
|
} else {
|
||||||
/**
|
await this.httpService.post(this.restPath + assignment.id + this.candidatureOtherPath, data);
|
||||||
* Removes an user from the list of candidates for an assignment
|
}
|
||||||
*
|
|
||||||
* @param user note: AssignmentUser, not a ViewUser
|
|
||||||
* @param assignment
|
|
||||||
*/
|
|
||||||
public async deleteCandidate(user: AssignmentUser, assignment: ViewAssignment): Promise<void> {
|
|
||||||
const restPath = `/rest/assignments/assignment/${assignment.id}/candidature_other/`;
|
|
||||||
const data = { user: user.id };
|
|
||||||
await this.httpService.delete(restPath, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the operator as candidate to the assignment
|
* Add the operator as candidate to the assignment
|
||||||
*
|
*
|
||||||
* @param assignment
|
* @param assignment The assignment to add the candidate to
|
||||||
*/
|
*/
|
||||||
public async addSelf(assignment: ViewAssignment): Promise<void> {
|
public async addSelf(assignment: ViewAssignment): Promise<void> {
|
||||||
const restPath = `/rest/assignments/assignment/${assignment.id}/candidature_self/`;
|
await this.httpService.post(this.restPath + assignment.id + this.candidatureSelfPath);
|
||||||
await this.httpService.post(restPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the current user (operator) from the list of candidates for an assignment
|
* Removes the current user (operator) from the list of candidates for an assignment
|
||||||
*
|
*
|
||||||
* @param assignment
|
* @param assignment The assignment to remove ourself from
|
||||||
*/
|
*/
|
||||||
public async deleteSelf(assignment: ViewAssignment): Promise<void> {
|
public async deleteSelf(assignment: ViewAssignment): Promise<void> {
|
||||||
const restPath = `/rest/assignments/assignment/${assignment.id}/candidature_self/`;
|
await this.httpService.delete(this.restPath + assignment.id + this.candidatureSelfPath);
|
||||||
await this.httpService.delete(restPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Poll to a given assignment
|
* Creates a new Poll to a given assignment
|
||||||
*
|
*
|
||||||
* @param assignment
|
* @param assignment The assignment to add the poll to
|
||||||
*/
|
*/
|
||||||
public async addPoll(assignment: ViewAssignment): Promise<void> {
|
public async addPoll(assignment: ViewAssignment): Promise<void> {
|
||||||
const restPath = `/rest/assignments/assignment/${assignment.id}/create_poll/`;
|
await this.httpService.post(this.restPath + assignment.id + this.createPollPath);
|
||||||
await this.httpService.post(restPath);
|
|
||||||
// TODO set phase, too, if phase was 0. Should be done server side?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -133,8 +128,7 @@ export class AssignmentRepositoryService extends BaseAgendaContentObjectReposito
|
|||||||
* @param id id of the poll to delete
|
* @param id id of the poll to delete
|
||||||
*/
|
*/
|
||||||
public async deletePoll(poll: Poll): Promise<void> {
|
public async deletePoll(poll: Poll): Promise<void> {
|
||||||
const restPath = `/rest/assignments/poll/${poll.id}/`;
|
await this.httpService.delete(`${this.restPollPath}${poll.id}/`);
|
||||||
await this.httpService.delete(restPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -143,12 +137,11 @@ export class AssignmentRepositoryService extends BaseAgendaContentObjectReposito
|
|||||||
* @param poll the (partial) data to update
|
* @param poll the (partial) data to update
|
||||||
* @param originalPoll the poll to update
|
* @param originalPoll the poll to update
|
||||||
*
|
*
|
||||||
* TODO check if votes is untouched
|
* TODO: check if votes is untouched
|
||||||
*/
|
*/
|
||||||
public async updatePoll(poll: Partial<Poll>, originalPoll: Poll): Promise<void> {
|
public async updatePoll(poll: Partial<Poll>, originalPoll: Poll): Promise<void> {
|
||||||
const restPath = `/rest/assignments/poll/${originalPoll.id}/`;
|
|
||||||
const data: Poll = Object.assign(originalPoll, poll);
|
const data: Poll = Object.assign(originalPoll, poll);
|
||||||
await this.httpService.patch(restPath, data);
|
await this.httpService.patch(`${this.restPollPath}${originalPoll.id}/`, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -186,8 +179,7 @@ export class AssignmentRepositoryService extends BaseAgendaContentObjectReposito
|
|||||||
votesno: null,
|
votesno: null,
|
||||||
votesvalid: poll.votesvalid || null
|
votesvalid: poll.votesvalid || null
|
||||||
};
|
};
|
||||||
const restPath = `/rest/assignments/poll/${originalPoll.id}/`;
|
await this.httpService.put(`${this.restPollPath}${originalPoll.id}/`, data);
|
||||||
await this.httpService.put(restPath, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -198,12 +190,11 @@ export class AssignmentRepositoryService extends BaseAgendaContentObjectReposito
|
|||||||
* @param elected true if the candidate is to be elected, false if unelected
|
* @param elected true if the candidate is to be elected, false if unelected
|
||||||
*/
|
*/
|
||||||
public async markElected(user: AssignmentUser, assignment: ViewAssignment, elected: boolean): Promise<void> {
|
public async markElected(user: AssignmentUser, assignment: ViewAssignment, elected: boolean): Promise<void> {
|
||||||
const restPath = `/rest/assignments/assignment/${assignment.id}/mark_elected/`;
|
|
||||||
const data = { user: user.user_id };
|
const data = { user: user.user_id };
|
||||||
if (elected) {
|
if (elected) {
|
||||||
await this.httpService.post(restPath, data);
|
await this.httpService.post(this.restPath + assignment.id + this.markElectedPath, data);
|
||||||
} else {
|
} else {
|
||||||
await this.httpService.delete(restPath, data);
|
await this.httpService.delete(this.restPath + assignment.id + this.markElectedPath, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ import { _ } from 'app/core/translate/translation-marker';
|
|||||||
* The possible keys of a poll object that represent numbers.
|
* The possible keys of a poll object that represent numbers.
|
||||||
* TODO Should be 'key of MotionPoll if type of key is number'
|
* TODO Should be 'key of MotionPoll if type of key is number'
|
||||||
* TODO: normalize MotionPoll model and other poll models
|
* TODO: normalize MotionPoll model and other poll models
|
||||||
* TODO: reuse more motion-poll-service stuff
|
|
||||||
*/
|
*/
|
||||||
export type CalculablePollKey = 'votesvalid' | 'votesinvalid' | 'votescast' | 'yes' | 'no' | 'abstain';
|
export type CalculablePollKey = 'votesvalid' | 'votesinvalid' | 'votescast' | 'yes' | 'no' | 'abstain';
|
||||||
|
|
||||||
@ -102,9 +101,9 @@ export abstract class PollService {
|
|||||||
/**
|
/**
|
||||||
* Gets an icon for a Poll Key
|
* Gets an icon for a Poll Key
|
||||||
*
|
*
|
||||||
* @param key
|
* @param key yes, no, abstain or something like that
|
||||||
* @returns a string for material-icons to represent the icon for
|
* @returns a string for material-icons to represent the icon for
|
||||||
* this key(e.g. yes: positiv sign, no: negative sign)
|
* this key(e.g. yes: positive sign, no: negative sign)
|
||||||
*/
|
*/
|
||||||
public getIcon(key: CalculablePollKey): string {
|
public getIcon(key: CalculablePollKey): string {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
@ -128,6 +127,7 @@ export abstract class PollService {
|
|||||||
/**
|
/**
|
||||||
* Gets a label for a poll Key
|
* Gets a label for a poll Key
|
||||||
*
|
*
|
||||||
|
* @param key yes, no, abstain or something like that
|
||||||
* @returns A short descriptive name for the poll keys
|
* @returns A short descriptive name for the poll keys
|
||||||
*/
|
*/
|
||||||
public getLabel(key: CalculablePollKey | PollVoteValue): string {
|
public getLabel(key: CalculablePollKey | PollVoteValue): string {
|
||||||
@ -151,11 +151,11 @@ export abstract class PollService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* retrieve special labels for a poll value
|
* retrieve special labels for a poll value
|
||||||
*
|
|
||||||
* @param value
|
|
||||||
* @returns the label for a non-positive value, according to
|
|
||||||
* {@link specialPollVotes}. Positive values will return as string
|
* {@link specialPollVotes}. Positive values will return as string
|
||||||
* representation of themselves
|
* representation of themselves
|
||||||
|
*
|
||||||
|
* @param value check value for special numbers
|
||||||
|
* @returns the label for a non-positive value, according to
|
||||||
*/
|
*/
|
||||||
public getSpecialLabel(value: number): string {
|
public getSpecialLabel(value: number): string {
|
||||||
if (value >= 0) {
|
if (value >= 0) {
|
||||||
@ -166,10 +166,9 @@ export abstract class PollService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the progressbar class for a decision key
|
* Get the progress bar class for a decision key
|
||||||
*
|
|
||||||
* @param key
|
|
||||||
*
|
*
|
||||||
|
* @param key a calculable poll key (like yes or no)
|
||||||
* @returns a css class designing a progress bar in a color, or an empty string
|
* @returns a css class designing a progress bar in a color, or an empty string
|
||||||
*/
|
*/
|
||||||
public getProgressBarColor(key: CalculablePollKey | PollVoteValue): string {
|
public getProgressBarColor(key: CalculablePollKey | PollVoteValue): string {
|
||||||
|
@ -32,10 +32,10 @@ export class PollOption extends Deserializer {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (input.votes) {
|
if (input.votes) {
|
||||||
input.votes = input.votes.map(v => {
|
input.votes = input.votes.map(vote => {
|
||||||
return {
|
return {
|
||||||
value: v.value,
|
value: vote.value,
|
||||||
weight: parseInt(v.weight, 10)
|
weight: parseInt(vote.weight, 10)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -33,11 +33,12 @@ export class Poll extends Deserializer {
|
|||||||
// cast stringify numbers
|
// cast stringify numbers
|
||||||
if (typeof input === 'object') {
|
if (typeof input === 'object') {
|
||||||
const numberifyKeys = ['id', 'votesvalid', 'votesinvalid', 'votescast', 'assignment_id'];
|
const numberifyKeys = ['id', 'votesvalid', 'votesinvalid', 'votescast', 'assignment_id'];
|
||||||
Object.keys(input).forEach(key => {
|
|
||||||
|
for (const key of Object.keys(input)) {
|
||||||
if (numberifyKeys.includes(key) && typeof input[key] === 'string') {
|
if (numberifyKeys.includes(key) && typeof input[key] === 'string') {
|
||||||
input[key] = parseInt(input[key], 10);
|
input[key] = parseInt(input[key], 10);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
super(input);
|
super(input);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
<os-head-bar [mainButton]="operator.hasPerms('assignments.can_manage')" (mainEvent)="onPlusButton()" [multiSelectMode]="isMultiSelect">
|
<os-head-bar
|
||||||
|
[mainButton]="operator.hasPerms('assignments.can_manage')"
|
||||||
|
(mainEvent)="onPlusButton()"
|
||||||
|
[multiSelectMode]="isMultiSelect"
|
||||||
|
>
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<div class="title-slot"><h2 translate>Elections</h2></div>
|
<div class="title-slot"><h2 translate>Elections</h2></div>
|
||||||
<!-- Menu -->
|
<!-- Menu -->
|
||||||
@ -91,6 +95,10 @@
|
|||||||
<mat-icon>done_all</mat-icon>
|
<mat-icon>done_all</mat-icon>
|
||||||
<span translate>Select all</span>
|
<span translate>Select all</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button mat-menu-item (click)="deselectAll()">
|
||||||
|
<mat-icon>clear</mat-icon>
|
||||||
|
<span translate>Deselect all</span>
|
||||||
|
</button>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<button
|
<button
|
||||||
mat-menu-item
|
mat-menu-item
|
||||||
|
@ -15,8 +15,7 @@ import { StorageService } from 'app/core/core-services/storage.service';
|
|||||||
import { ViewAssignment } from '../models/view-assignment';
|
import { ViewAssignment } from '../models/view-assignment';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listview for the assignments
|
* List view for the assignments
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'os-assignment-list',
|
selector: 'os-assignment-list',
|
||||||
@ -26,7 +25,9 @@ import { ViewAssignment } from '../models/view-assignment';
|
|||||||
export class AssignmentListComponent extends ListViewBaseComponent<ViewAssignment, Assignment> implements OnInit {
|
export class AssignmentListComponent extends ListViewBaseComponent<ViewAssignment, Assignment> implements OnInit {
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
|
*
|
||||||
* @param titleService
|
* @param titleService
|
||||||
|
* @param storage
|
||||||
* @param translate
|
* @param translate
|
||||||
* @param matSnackBar
|
* @param matSnackBar
|
||||||
* @param repo the repository
|
* @param repo the repository
|
||||||
@ -51,7 +52,7 @@ export class AssignmentListComponent extends ListViewBaseComponent<ViewAssignmen
|
|||||||
public operator: OperatorService
|
public operator: OperatorService
|
||||||
) {
|
) {
|
||||||
super(titleService, translate, matSnackBar, route, storage, filterService, sortService);
|
super(titleService, translate, matSnackBar, route, storage, filterService, sortService);
|
||||||
// activate multiSelect mode for this listview
|
// activate multiSelect mode for this list view
|
||||||
this.canMultiSelect = true;
|
this.canMultiSelect = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,10 +3,10 @@ import { NgModule } from '@angular/core';
|
|||||||
|
|
||||||
import { AssignmentDetailComponent } from './components/assignment-detail/assignment-detail.component';
|
import { AssignmentDetailComponent } from './components/assignment-detail/assignment-detail.component';
|
||||||
import { AssignmentListComponent } from './assignment-list/assignment-list.component';
|
import { AssignmentListComponent } from './assignment-list/assignment-list.component';
|
||||||
import { AssignmentsRoutingModule } from './assignments-routing.module';
|
|
||||||
import { SharedModule } from '../../shared/shared.module';
|
|
||||||
import { AssignmentPollComponent } from './components/assignment-poll/assignment-poll.component';
|
import { AssignmentPollComponent } from './components/assignment-poll/assignment-poll.component';
|
||||||
import { AssignmentPollDialogComponent } from './components/assignment-poll/assignment-poll-dialog.component';
|
import { AssignmentPollDialogComponent } from './components/assignment-poll/assignment-poll-dialog.component';
|
||||||
|
import { AssignmentsRoutingModule } from './assignments-routing.module';
|
||||||
|
import { SharedModule } from '../../shared/shared.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [CommonModule, AssignmentsRoutingModule, SharedModule],
|
imports: [CommonModule, AssignmentsRoutingModule, SharedModule],
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
import { Router, ActivatedRoute } from '@angular/router';
|
import { BehaviorSubject } from 'rxjs';
|
||||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||||
import { MatSnackBar, MatSelectChange } from '@angular/material';
|
import { MatSnackBar, MatSelectChange } from '@angular/material';
|
||||||
|
import { Router, ActivatedRoute } from '@angular/router';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { BehaviorSubject } from 'rxjs';
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { BaseViewComponent } from '../../../base/base-view';
|
|
||||||
|
|
||||||
import { Assignment } from 'app/shared/models/assignments/assignment';
|
import { Assignment } from 'app/shared/models/assignments/assignment';
|
||||||
import { AssignmentPollService } from '../../services/assignment-poll.service';
|
import { AssignmentPollService } from '../../services/assignment-poll.service';
|
||||||
import { AssignmentRepositoryService } from 'app/core/repositories/assignments/assignment-repository.service';
|
import { AssignmentRepositoryService } from 'app/core/repositories/assignments/assignment-repository.service';
|
||||||
import { AssignmentUser } from 'app/shared/models/assignments/assignment-user';
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
import { ConstantsService } from 'app/core/ui-services/constants.service';
|
import { ConstantsService } from 'app/core/ui-services/constants.service';
|
||||||
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
|
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
|
||||||
import { LocalPermissionsService } from 'app/site/motions/services/local-permissions.service';
|
import { LocalPermissionsService } from 'app/site/motions/services/local-permissions.service';
|
||||||
@ -34,7 +32,7 @@ import { ViewUser } from 'app/site/users/models/view-user';
|
|||||||
templateUrl: './assignment-detail.component.html',
|
templateUrl: './assignment-detail.component.html',
|
||||||
styleUrls: ['./assignment-detail.component.scss']
|
styleUrls: ['./assignment-detail.component.scss']
|
||||||
})
|
})
|
||||||
export class AssignmentDetailComponent extends BaseViewComponent implements OnInit, OnDestroy {
|
export class AssignmentDetailComponent extends BaseViewComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* Determines if the assignment is new
|
* Determines if the assignment is new
|
||||||
*/
|
*/
|
||||||
@ -72,17 +70,17 @@ export class AssignmentDetailComponent extends BaseViewComponent implements OnIn
|
|||||||
public assignmentForm: FormGroup;
|
public assignmentForm: FormGroup;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used in the seacrhValue selector to assign tags
|
* Used in the search Value selector to assign tags
|
||||||
*/
|
*/
|
||||||
public tagsObserver: BehaviorSubject<ViewTag[]>;
|
public tagsObserver: BehaviorSubject<ViewTag[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used in the seacrhValue selector to assign an agenda item
|
* Used in the search Value selector to assign an agenda item
|
||||||
*/
|
*/
|
||||||
public agendaObserver: BehaviorSubject<ViewItem[]>;
|
public agendaObserver: BehaviorSubject<ViewItem[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the assignment, e.g. via an autoupdate. Reload important things here:
|
* Sets the assignment, e.g. via an auto update. Reload important things here:
|
||||||
* - Poll base values are be recalculated
|
* - Poll base values are be recalculated
|
||||||
*
|
*
|
||||||
* @param assignment the assignment to set
|
* @param assignment the assignment to set
|
||||||
@ -253,7 +251,7 @@ export class AssignmentDetailComponent extends BaseViewComponent implements OnIn
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: change/update the assignment form values
|
* Changes/updates the assignment form values
|
||||||
*
|
*
|
||||||
* @param assignment
|
* @param assignment
|
||||||
*/
|
*/
|
||||||
@ -282,7 +280,7 @@ export class AssignmentDetailComponent extends BaseViewComponent implements OnIn
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Poll
|
* Creates a new Poll
|
||||||
* TODO: directly open poll dialog
|
* TODO: directly open poll dialog?
|
||||||
*/
|
*/
|
||||||
public async createPoll(): Promise<void> {
|
public async createPoll(): Promise<void> {
|
||||||
await this.repo.addPoll(this.assignment).then(null, this.raiseError);
|
await this.repo.addPoll(this.assignment).then(null, this.raiseError);
|
||||||
@ -304,14 +302,12 @@ export class AssignmentDetailComponent extends BaseViewComponent implements OnIn
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a user to the list of candidates
|
* Adds a user to the list of candidates
|
||||||
*
|
|
||||||
* @param user
|
|
||||||
*/
|
*/
|
||||||
public async addUser(): Promise<void> {
|
public async addUser(): Promise<void> {
|
||||||
const candId = this.candidatesForm.get('candidate').value;
|
const candId = this.candidatesForm.get('candidate').value;
|
||||||
this.candidatesForm.setValue({ candidate: null });
|
this.candidatesForm.setValue({ candidate: null });
|
||||||
if (candId) {
|
if (candId) {
|
||||||
await this.repo.addCandidate(candId, this.assignment).then(null, this.raiseError);
|
await this.repo.changeCandidate(candId, this.assignment).then(null, this.raiseError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,8 +316,8 @@ export class AssignmentDetailComponent extends BaseViewComponent implements OnIn
|
|||||||
*
|
*
|
||||||
* @param user Assignment User
|
* @param user Assignment User
|
||||||
*/
|
*/
|
||||||
public async removeUser(user: AssignmentUser): Promise<void> {
|
public async removeUser(user: ViewUser): Promise<void> {
|
||||||
await this.repo.deleteCandidate(user, this.assignment).then(null, this.raiseError);
|
await this.repo.changeCandidate(user.id, this.assignment).then(null, this.raiseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -343,7 +339,7 @@ export class AssignmentDetailComponent extends BaseViewComponent implements OnIn
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.newAssignment = true;
|
this.newAssignment = true;
|
||||||
// TODO set defaults
|
// TODO set defaults?
|
||||||
this.assignment = new ViewAssignment(new Assignment());
|
this.assignment = new ViewAssignment(new Assignment());
|
||||||
this.patchForm(this.assignment);
|
this.patchForm(this.assignment);
|
||||||
this.setEditMode(true);
|
this.setEditMode(true);
|
||||||
|
@ -63,12 +63,11 @@
|
|||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
matInput
|
matInput
|
||||||
[value]="getSumValue('votestotal')"
|
[value]="getSumValue('votescast')"
|
||||||
(change)="setSumValue('votestotal', $event.target.value)"
|
(change)="setSumValue('votescast', $event.target.value)"
|
||||||
/>
|
/>
|
||||||
<mat-label translate>Total votes</mat-label>
|
<mat-label translate>Total votes</mat-label>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="submit-buttons">
|
<div class="submit-buttons">
|
||||||
<button mat-button (click)="submit()">{{ 'Save' | translate }}</button>
|
<button mat-button (click)="submit()">{{ 'Save' | translate }}</button>
|
||||||
|
@ -69,7 +69,7 @@ export class AssignmentPollDialogComponent {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates candidates input (every candidate has their options filled in),
|
* Validates candidates input (every candidate has their options filled in),
|
||||||
* submits and closes the dialog if successfull, else displays an error popup.
|
* submits and closes the dialog if successful, else displays an error popup.
|
||||||
* TODO better validation
|
* TODO better validation
|
||||||
*/
|
*/
|
||||||
public submit(): void {
|
public submit(): void {
|
||||||
@ -123,14 +123,14 @@ export class AssignmentPollDialogComponent {
|
|||||||
* @param candidate the candidate for whom to update the value
|
* @param candidate the candidate for whom to update the value
|
||||||
* @param newData the new value
|
* @param newData the new value
|
||||||
*/
|
*/
|
||||||
public setValue(value: PollVoteValue, candidate: PollOption, newData: number): void {
|
public setValue(value: PollVoteValue, candidate: PollOption, newData: string): void {
|
||||||
const vote = candidate.votes.find(v => v.value === value);
|
const vote = candidate.votes.find(v => v.value === value);
|
||||||
if (vote) {
|
if (vote) {
|
||||||
vote.weight = +newData;
|
vote.weight = parseInt(newData, 10);
|
||||||
} else {
|
} else {
|
||||||
candidate.votes.push({
|
candidate.votes.push({
|
||||||
value: value,
|
value: value,
|
||||||
weight: +newData
|
weight: parseInt(newData, 10)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,7 +142,7 @@ export class AssignmentPollDialogComponent {
|
|||||||
* @param candidate the pollOption
|
* @param candidate the pollOption
|
||||||
* @returns the currently entered number or undefined if no number has been set
|
* @returns the currently entered number or undefined if no number has been set
|
||||||
*/
|
*/
|
||||||
public getValue(value: PollVoteValue, candidate: PollOption): number {
|
public getValue(value: PollVoteValue, candidate: PollOption): number | undefined {
|
||||||
const val = candidate.votes.find(v => v.value === value);
|
const val = candidate.votes.find(v => v.value === value);
|
||||||
return val ? val.weight : undefined;
|
return val ? val.weight : undefined;
|
||||||
}
|
}
|
||||||
@ -151,10 +151,10 @@ export class AssignmentPollDialogComponent {
|
|||||||
* Retrieves a per-poll value
|
* Retrieves a per-poll value
|
||||||
*
|
*
|
||||||
* @param value
|
* @param value
|
||||||
* @returns integer or null
|
* @returns integer or undefined
|
||||||
*/
|
*/
|
||||||
public getSumValue(value: summaryPollKeys): number | null {
|
public getSumValue(value: summaryPollKeys): number | undefined {
|
||||||
return this.data.poll[value] || null;
|
return this.data.poll[value] || undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -164,6 +164,6 @@ export class AssignmentPollDialogComponent {
|
|||||||
* @param weight
|
* @param weight
|
||||||
*/
|
*/
|
||||||
public setSumValue(value: summaryPollKeys, weight: string): void {
|
public setSumValue(value: summaryPollKeys, weight: string): void {
|
||||||
this.data.poll[value] = +weight;
|
this.data.poll[value] = parseInt(weight, 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Component, OnInit, Input } from '@angular/core';
|
import { Component, OnInit, Input } from '@angular/core';
|
||||||
import { MatDialog } from '@angular/material';
|
import { MatDialog, MatSnackBar } from '@angular/material';
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
@ -12,17 +12,18 @@ import { Poll } from 'app/shared/models/assignments/poll';
|
|||||||
import { PollOption } from 'app/shared/models/assignments/poll-option';
|
import { PollOption } from 'app/shared/models/assignments/poll-option';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { ViewAssignment } from '../../models/view-assignment';
|
import { ViewAssignment } from '../../models/view-assignment';
|
||||||
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
|
import { Title } from '@angular/platform-browser';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for a single assignment poll. Used in assignment detail view
|
* Component for a single assignment poll. Used in assignment detail view
|
||||||
* TODO DOCU
|
|
||||||
*/
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'os-assignment-poll',
|
selector: 'os-assignment-poll',
|
||||||
templateUrl: './assignment-poll.component.html',
|
templateUrl: './assignment-poll.component.html',
|
||||||
styleUrls: ['./assignment-poll.component.scss']
|
styleUrls: ['./assignment-poll.component.scss']
|
||||||
})
|
})
|
||||||
export class AssignmentPollComponent implements OnInit {
|
export class AssignmentPollComponent extends BaseViewComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* The related assignment (used for metainfos, e.g. related user names)
|
* The related assignment (used for metainfos, e.g. related user names)
|
||||||
*/
|
*/
|
||||||
@ -52,6 +53,8 @@ export class AssignmentPollComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Gets the voting options
|
||||||
|
*
|
||||||
* @returns all used (not undefined) option-independent values that are
|
* @returns all used (not undefined) option-independent values that are
|
||||||
* used in this poll (e.g.)
|
* used in this poll (e.g.)
|
||||||
*/
|
*/
|
||||||
@ -60,6 +63,8 @@ export class AssignmentPollComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Gets the translated poll method name
|
||||||
|
*
|
||||||
* TODO: check/improve text here
|
* TODO: check/improve text here
|
||||||
*
|
*
|
||||||
* @returns a name for the poll method this poll is set to (which is determined
|
* @returns a name for the poll method this poll is set to (which is determined
|
||||||
@ -92,15 +97,20 @@ export class AssignmentPollComponent implements OnInit {
|
|||||||
* @param promptService Prompts for confirmation dialogs
|
* @param promptService Prompts for confirmation dialogs
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
|
titleService: Title,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
public pollService: AssignmentPollService,
|
public pollService: AssignmentPollService,
|
||||||
private operator: OperatorService,
|
private operator: OperatorService,
|
||||||
private assignmentRepo: AssignmentRepositoryService,
|
private assignmentRepo: AssignmentRepositoryService,
|
||||||
public translate: TranslateService,
|
public translate: TranslateService,
|
||||||
public dialog: MatDialog,
|
public dialog: MatDialog,
|
||||||
private promptService: PromptService
|
private promptService: PromptService
|
||||||
) {}
|
) {
|
||||||
|
super(titleService, translate, matSnackBar);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Gets the currently selected majority choice option from the repo
|
||||||
*/
|
*/
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.majorityChoice =
|
this.majorityChoice =
|
||||||
@ -116,17 +126,17 @@ export class AssignmentPollComponent implements OnInit {
|
|||||||
public async onDeletePoll(): Promise<void> {
|
public async onDeletePoll(): Promise<void> {
|
||||||
const title = this.translate.instant('Are you sure you want to delete this poll?');
|
const title = this.translate.instant('Are you sure you want to delete this poll?');
|
||||||
if (await this.promptService.open(title, null)) {
|
if (await this.promptService.open(title, null)) {
|
||||||
await this.assignmentRepo.deletePoll(this.poll);
|
await this.assignmentRepo.deletePoll(this.poll).then(null, this.raiseError);
|
||||||
}
|
}
|
||||||
// TODO error handling
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Print the PDF of this poll with the corresponding options and numbers
|
||||||
|
*
|
||||||
* TODO Print the ballots for this poll.
|
* TODO Print the ballots for this poll.
|
||||||
*/
|
*/
|
||||||
public printBallot(poll: Poll): void {
|
public printBallot(poll: Poll): void {
|
||||||
this.promptService.open('TODO', 'TODO');
|
this.raiseError('Not yet implemented');
|
||||||
// TODO Print ballot not implemented
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -136,7 +146,7 @@ export class AssignmentPollComponent implements OnInit {
|
|||||||
* @returns the full_name for the candidate
|
* @returns the full_name for the candidate
|
||||||
*/
|
*/
|
||||||
public getCandidateName(option: PollOption): string {
|
public getCandidateName(option: PollOption): string {
|
||||||
const user = this.assignment.candidates.find(c => c.id === option.candidate_id);
|
const user = this.assignment.candidates.find(candidate => candidate.id === option.candidate_id);
|
||||||
return user ? user.full_name : '';
|
return user ? user.full_name : '';
|
||||||
// TODO this.assignment.candidates may not contain every candidates' name (if deleted later)
|
// TODO this.assignment.candidates may not contain every candidates' name (if deleted later)
|
||||||
// so we should rather use this.userRepo.getViewModel(option.id).full_name
|
// so we should rather use this.userRepo.getViewModel(option.id).full_name
|
||||||
@ -161,7 +171,6 @@ export class AssignmentPollComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* Opens the {@link AssignmentPollDialogComponent} dialog and then updates the votes, if the dialog
|
* Opens the {@link AssignmentPollDialogComponent} dialog and then updates the votes, if the dialog
|
||||||
* closes successfully (validation is done there)
|
* closes successfully (validation is done there)
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public enterVotes(): void {
|
public enterVotes(): void {
|
||||||
// TODO deep copy of this.poll (JSON parse is ugly workaround)
|
// TODO deep copy of this.poll (JSON parse is ugly workaround)
|
||||||
@ -174,13 +183,12 @@ export class AssignmentPollComponent implements OnInit {
|
|||||||
data: data,
|
data: data,
|
||||||
maxHeight: '90vh',
|
maxHeight: '90vh',
|
||||||
minWidth: '300px',
|
minWidth: '300px',
|
||||||
maxWidth: '80vh',
|
maxWidth: '80vw',
|
||||||
disableClose: true
|
disableClose: true
|
||||||
});
|
});
|
||||||
dialogRef.afterClosed().subscribe(result => {
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
if (result) {
|
if (result) {
|
||||||
this.assignmentRepo.updateVotes(result, this.poll);
|
this.assignmentRepo.updateVotes(result, this.poll).then(null, this.raiseError);
|
||||||
// TODO error handling
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -188,6 +196,7 @@ export class AssignmentPollComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* Updates the majority method for this poll
|
* Updates the majority method for this poll
|
||||||
*
|
*
|
||||||
|
* @param method the selected majority method
|
||||||
*/
|
*/
|
||||||
public setMajority(method: MajorityMethod): void {
|
public setMajority(method: MajorityMethod): void {
|
||||||
this.majorityChoice = method;
|
this.majorityChoice = method;
|
||||||
@ -209,9 +218,10 @@ export class AssignmentPollComponent implements OnInit {
|
|||||||
if (!this.operator.hasPerms('assignments.can_manage')) {
|
if (!this.operator.hasPerms('assignments.can_manage')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO additional conditions: assignment not finished?
|
// TODO additional conditions: assignment not finished?
|
||||||
const candidate = this.assignment.assignment.assignment_related_users.find(
|
const candidate = this.assignment.assignment.assignment_related_users.find(
|
||||||
u => u.user_id === option.candidate_id
|
user => user.user_id === option.candidate_id
|
||||||
);
|
);
|
||||||
if (candidate) {
|
if (candidate) {
|
||||||
this.assignmentRepo.markElected(candidate, this.assignment, !option.is_elected);
|
this.assignmentRepo.markElected(candidate, this.assignment, !option.is_elected);
|
||||||
|
@ -69,6 +69,9 @@ export class ViewAssignment extends BaseAgendaViewModel {
|
|||||||
|
|
||||||
public constructor(assignment: Assignment, relatedUser?: ViewUser[], agendaItem?: ViewItem, tags?: ViewTag[]) {
|
public constructor(assignment: Assignment, relatedUser?: ViewUser[], agendaItem?: ViewItem, tags?: ViewTag[]) {
|
||||||
super(Assignment.COLLECTIONSTRING);
|
super(Assignment.COLLECTIONSTRING);
|
||||||
|
|
||||||
|
console.log('related user: ', relatedUser);
|
||||||
|
|
||||||
this._assignment = assignment;
|
this._assignment = assignment;
|
||||||
this._relatedUser = relatedUser;
|
this._relatedUser = relatedUser;
|
||||||
this._agendaItem = agendaItem;
|
this._agendaItem = agendaItem;
|
||||||
|
@ -16,7 +16,7 @@ export type AssignmentPollMethod = 'yn' | 'yna' | 'votes';
|
|||||||
type AssignmentPercentBase = 'YES_NO_ABSTAIN' | 'YES_NO' | 'VALID' | 'CAST' | 'DISABLED';
|
type AssignmentPercentBase = 'YES_NO_ABSTAIN' | 'YES_NO' | 'VALID' | 'CAST' | 'DISABLED';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service class for motion polls.
|
* Service class for assignment polls.
|
||||||
*/
|
*/
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -46,6 +46,7 @@ export class AssignmentPollService extends PollService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor. Subscribes to the configuration values needed
|
* Constructor. Subscribes to the configuration values needed
|
||||||
|
*
|
||||||
* @param config ConfigService
|
* @param config ConfigService
|
||||||
*/
|
*/
|
||||||
public constructor(config: ConfigService) {
|
public constructor(config: ConfigService) {
|
||||||
@ -59,12 +60,11 @@ export class AssignmentPollService extends PollService {
|
|||||||
config
|
config
|
||||||
.get<AssignmentPercentBase>('assignments_poll_100_percent_base')
|
.get<AssignmentPercentBase>('assignments_poll_100_percent_base')
|
||||||
.subscribe(base => (this.percentBase = base));
|
.subscribe(base => (this.percentBase = base));
|
||||||
// assignments_add_candidates_to_list_of_speakers boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the base amount for the 100% calculations. Note that some poll methods
|
* Get the base amount for the 100% calculations. Note that some poll methods
|
||||||
* (e.g. yes/no/abstain may have a diffferent percentage base and will return null here)
|
* (e.g. yes/no/abstain may have a different percentage base and will return null here)
|
||||||
*
|
*
|
||||||
* @param poll
|
* @param poll
|
||||||
* @returns The amount of votes indicating the 100% base
|
* @returns The amount of votes indicating the 100% base
|
||||||
@ -120,6 +120,8 @@ export class AssignmentPollService extends PollService {
|
|||||||
/**
|
/**
|
||||||
* Check if the option in a poll is abstract (percentages should not be calculated)
|
* Check if the option in a poll is abstract (percentages should not be calculated)
|
||||||
*
|
*
|
||||||
|
* @param poll
|
||||||
|
* @param option
|
||||||
* @returns true if the poll has no percentages, the poll option is a special value,
|
* @returns true if the poll has no percentages, the poll option is a special value,
|
||||||
* or if the calculations are disabled in the config
|
* or if the calculations are disabled in the config
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user