Improved UI of motion list and detail view
This commit is contained in:
parent
86e858c800
commit
ed47ac4884
@ -131,19 +131,19 @@ export class HttpService {
|
||||
|
||||
/**
|
||||
* Exectures a post on a url with a certain object
|
||||
* @param url string of the url to send semothing to
|
||||
* @param data The data to send
|
||||
* @param url The url to send the request to.
|
||||
* @param data An optional payload for the request.
|
||||
* @param header optional HTTP header if required
|
||||
* @returns A promise holding a generic
|
||||
*/
|
||||
public async post<T>(url: string, data: any, header?: HttpHeaders): Promise<T> {
|
||||
public async post<T>(url: string, data?: any, header?: HttpHeaders): Promise<T> {
|
||||
return await this.send<T>(url, HTTPMethod.POST, data, header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exectures a put on a url with a certain object
|
||||
* @param url string of the url to send semothing to
|
||||
* @param data the object that should be send
|
||||
* @param url The url to send the request to.
|
||||
* @param data The payload for the request.
|
||||
* @param header optional HTTP header if required
|
||||
* @returns A promise holding a generic
|
||||
*/
|
||||
@ -153,8 +153,8 @@ export class HttpService {
|
||||
|
||||
/**
|
||||
* Exectures a put on a url with a certain object
|
||||
* @param url the url that should be called
|
||||
* @param data: The data to send
|
||||
* @param url The url to send the request to.
|
||||
* @param data: The payload for the request.
|
||||
* @param header optional HTTP header if required
|
||||
* @returns A promise holding a generic
|
||||
*/
|
||||
@ -164,7 +164,7 @@ export class HttpService {
|
||||
|
||||
/**
|
||||
* Makes a delete request.
|
||||
* @param url the url that should be called
|
||||
* @param url The url to send the request to.
|
||||
* @param data An optional data to send in the requestbody.
|
||||
* @param header optional HTTP header if required
|
||||
* @returns A promise holding a generic
|
||||
|
@ -79,7 +79,7 @@
|
||||
<mat-accordion multi='true' class='on-transition-fade'>
|
||||
|
||||
<!-- MetaInfo Panel-->
|
||||
<mat-expansion-panel #metaInfoPanel [expanded]="editMotion" class='meta-info-block meta-info-panel'>
|
||||
<mat-expansion-panel #metaInfoPanel [expanded]="true" class='meta-info-block meta-info-panel'>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
<mat-icon>info</mat-icon>
|
||||
@ -93,9 +93,6 @@
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<os-motion-comments *ngIf="!newMotion" [motion]="motion"></os-motion-comments>
|
||||
<os-personal-note *ngIf="!newMotion" [motion]="motion"></os-personal-note>
|
||||
|
||||
<!-- Content -->
|
||||
<mat-expansion-panel #contentPanel [expanded]='true'>
|
||||
<mat-expansion-panel-header>
|
||||
@ -109,6 +106,9 @@
|
||||
<ng-container *ngTemplateOutlet="contentTemplate"></ng-container>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<os-motion-comments *ngIf="!newMotion" [motion]="motion"></os-motion-comments>
|
||||
<os-personal-note *ngIf="!newMotion" [motion]="motion"></os-personal-note>
|
||||
</mat-accordion>
|
||||
</ng-template>
|
||||
|
||||
@ -150,7 +150,7 @@
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<!-- Submitter -->
|
||||
<!-- Submitters -->
|
||||
<div *ngIf="motion && motion.submitters || newMotion">
|
||||
<div *ngIf="newMotion">
|
||||
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
|
||||
@ -166,75 +166,97 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Supporter -->
|
||||
<div *ngIf='motion && motion.hasSupporters() || editMotion'>
|
||||
<!-- print all motion supporters -->
|
||||
<!-- Supporters -->
|
||||
<div *ngIf='motion && minSupporters'>
|
||||
<div *ngIf="editMotion">
|
||||
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
|
||||
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="metaInfoForm.get('supporters_id')"
|
||||
[multiple]="true" listname="{{ 'Supporters' | translate }}" [InputListValues]="supporterObserver"></os-search-value-selector>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="!editMotion && motion.hasSupporters()">
|
||||
<h4 translate>Supporters</h4>
|
||||
<ul *ngFor="let supporter of motion.supporters">
|
||||
<li>{{ supporter.full_name }}</li>
|
||||
</ul>
|
||||
<div *ngIf="!editMotion">
|
||||
<h4 *ngIf="perms.isAllowed('support', motion) || motion.hasSupporters()" translate>Supporters</h4>
|
||||
<!-- support button -->
|
||||
<button type="button" *ngIf="perms.isAllowed('support', motion)" (click)=support() mat-raised-button color="primary">
|
||||
<mat-icon>thumb_up</mat-icon>
|
||||
{{ 'Support' | translate }}
|
||||
</button>
|
||||
<!-- unsupport button -->
|
||||
<button type="button" *ngIf="perms.isAllowed('unsupport', motion)" (click)=unsupport() mat-raised-button color="primary">
|
||||
<mat-icon>thumb_down</mat-icon>
|
||||
{{ 'Unsupport' | translate }}
|
||||
</button>
|
||||
<!-- show supporters (TODO: open in dialog) -->
|
||||
<button type="button" *ngIf="motion.hasSupporters()" (click)=openSupportersDialog() mat-button>
|
||||
{{ motion.supporters.length }} {{ 'supporters' | translate }}
|
||||
</button>
|
||||
<p *ngIf="showSupporters">
|
||||
<mat-chip-list *ngFor="let supporter of motion.supporters">
|
||||
<mat-chip>{{ supporter.full_name }}</mat-chip>
|
||||
</mat-chip-list>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- State -->
|
||||
<div *ngIf='motion && motion.state'>
|
||||
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata'];complement:true">
|
||||
<ng-container *ngIf="!newMotion">
|
||||
<h4 translate>State</h4>
|
||||
{{ motion.state }}
|
||||
</ng-container>
|
||||
</div>
|
||||
<div *ngIf="!editMotion">
|
||||
<mat-form-field *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
|
||||
<mat-select placeholder="{{ 'State' | translate }}" formControlName='state_id' (selectionChange)="onChangeState($event)">
|
||||
<mat-option [value]="motion.state_id">{{ motion.state }}</mat-option>
|
||||
<mat-divider></mat-divider>
|
||||
<mat-option *ngFor="let state of motion.nextStates" [value]="state.id">{{ state }}</mat-option>
|
||||
<mat-divider></mat-divider>
|
||||
<mat-option [value]="null">
|
||||
<mat-icon>replay</mat-icon>
|
||||
<span translate>Reset State</span>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div *ngIf='motion && !editMotion'>
|
||||
<h4 translate>State</h4>
|
||||
<mat-menu #stateMenu='matMenu'>
|
||||
<button *ngFor='let state of motion.nextStates' mat-menu-item
|
||||
(click)=setState(state.id)>{{ state.name | translate }}
|
||||
</button>
|
||||
<mat-divider></mat-divider>
|
||||
<button mat-menu-item (click)=setState(null)>
|
||||
<mat-icon>replay</mat-icon> {{ 'Reset state' | translate }}
|
||||
</button>
|
||||
</mat-menu>
|
||||
<mat-basic-chip [matMenuTriggerFor]='stateMenu' [ngClass]="{
|
||||
'green': motion.state.css_class === 'success',
|
||||
'red': motion.state.css_class === 'danger',
|
||||
'grey': motion.state.css_class === 'default',
|
||||
'lightblue': motion.state.css_class === 'primary' }">
|
||||
{{ motion.state.name | translate }}
|
||||
</mat-basic-chip>
|
||||
|
||||
<!--*osPerms="['motions.can_manage', 'motions.can_manage_metadata']; -->
|
||||
</div>
|
||||
|
||||
<!-- Recommendation -->
|
||||
<div *ngIf='motion && motion.state && recommender'>
|
||||
<mat-form-field *ngIf="!editMotion && !newMotion">
|
||||
<mat-select [placeholder]=recommender formControlName='recommendation_id' (selectionChange)="onChangerRecommenderState($event)">
|
||||
<mat-option *ngFor="let recommendation of motion.possibleRecommendations" [value]="recommendation.id">
|
||||
{{ recommendation.recommendation_label | translate }}
|
||||
</mat-option>
|
||||
<mat-divider></mat-divider>
|
||||
<mat-option [value]="null">
|
||||
<mat-icon>replay</mat-icon><span translate>Reset recommendation</span>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<div *ngIf='motion && recommender && !editMotion'>
|
||||
<h4 translate>{{ recommender }}</h4>
|
||||
<mat-menu #recommendationMenu='matMenu'>
|
||||
<button *ngFor='let recommendation of motion.possibleRecommendations' mat-menu-item
|
||||
(click)=setRecommendation(recommendation.id)>{{ recommendation.recommendation_label | translate }}
|
||||
</button>
|
||||
<mat-divider></mat-divider>
|
||||
<button mat-menu-item (click)=setRecommendation(null)>
|
||||
<mat-icon>replay</mat-icon> {{ 'Reset recommendation' | translate }}
|
||||
</button>
|
||||
</mat-menu>
|
||||
<mat-basic-chip [matMenuTriggerFor]='recommendationMenu' class="bluegrey">
|
||||
{{ motion.recommendation ? (motion.recommendation.recommendation_label | translate) : ('not set' | translate) }}
|
||||
</mat-basic-chip>
|
||||
</div>
|
||||
|
||||
<!-- Category -->
|
||||
<div *ngIf="motion && motion.category_id || editMotion">
|
||||
<div *ngIf='!editMotion'>
|
||||
<h4 translate>Category</h4>
|
||||
{{ motion.category }}
|
||||
</div>
|
||||
<div *ngIf="editMotion || newMotion">
|
||||
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="metaInfoForm.get('category_id')"
|
||||
[multiple]="false" listname="{{ 'Category' | translate }}" [InputListValues]="categoryObserver" includeNone="true"></os-search-value-selector>
|
||||
</div>
|
||||
<!-- Disabled during "new motion" since changing has no effect -->
|
||||
<div *ngIf="motion && !editMotion">
|
||||
<h4 translate>Category</h4>
|
||||
<mat-menu #categoryMenu='matMenu'>
|
||||
<button *ngFor='let category of categoryObserver.value' mat-menu-item
|
||||
(click)=setCategory(category.id)>{{ category }}
|
||||
</button>
|
||||
<button mat-menu-item (click)=setCategory(null)>
|
||||
---
|
||||
</button>
|
||||
</mat-menu>
|
||||
<mat-basic-chip [matMenuTriggerFor]='categoryMenu' class="grey">
|
||||
{{ motion.category ? motion.category : ('not set' | translate) }}
|
||||
</mat-basic-chip>
|
||||
</div>
|
||||
|
||||
<!-- Workflow (just during creation) -->
|
||||
<!-- Workflow -->
|
||||
<div *ngIf="editMotion">
|
||||
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
|
||||
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="metaInfoForm.get('workflow_id')"
|
||||
@ -270,7 +292,7 @@
|
||||
<button type="button" mat-icon-button [matMenuTriggerFor]="lineNumberingMenu" matTooltip="{{ 'Line numbering' | translate }}">
|
||||
<mat-icon>format_list_numbered</mat-icon>
|
||||
</button>
|
||||
<button type="button" mat-icon-button [matMenuTriggerFor]="changeRecoMenu" matTooltip="{{ 'Change recommendations' | translate }}">
|
||||
<button *ngIf="allChangingObjects.length > 0" type="button" mat-icon-button [matMenuTriggerFor]="changeRecoMenu" matTooltip="{{ 'Change recommendations' | translate }}">
|
||||
<mat-icon>rate_review</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
@ -293,21 +315,15 @@
|
||||
</div>
|
||||
|
||||
<!-- Title -->
|
||||
<div *ngIf="motion && motion.title || editMotion">
|
||||
<div *ngIf='!editMotion'>
|
||||
<h4>{{motion.title}}</h4>
|
||||
</div>
|
||||
|
||||
<mat-form-field *ngIf="editMotion" class="wide-form">
|
||||
<input matInput osAutofocus placeholder="{{ 'Title' | translate }}" formControlName='title' [value]='motionCopy.title'
|
||||
required>
|
||||
<div *ngIf="motion && editMotion">
|
||||
<mat-form-field class="wide-form">
|
||||
<input matInput osAutofocus placeholder="{{ 'Title' | translate }}"
|
||||
formControlName='title' [value]='motionCopy.title' required>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Text -->
|
||||
<!-- TODO: this is a config variable. Read it out -->
|
||||
<span class="text-prefix-label" translate>The assembly may decide:</span>
|
||||
<span class="text-prefix-label">{{ preamble | translate }}</span>
|
||||
<ng-container *ngIf='motion && !editMotion && !motion.isStatuteAmendment()'>
|
||||
<div *ngIf="!isRecoModeDiff()" class="motion-text" [class.line-numbers-none]="isLineNumberingNone()"
|
||||
[class.line-numbers-inline]="isLineNumberingInline()" [class.line-numbers-outside]="isLineNumberingOutside()">
|
||||
@ -343,17 +359,18 @@
|
||||
[init]="tinyMceSettings"
|
||||
*ngIf="editMotion"
|
||||
></editor>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</ng-template>
|
||||
|
||||
<!-- Line number Menu -->
|
||||
<mat-menu #lineNumberingMenu="matMenu" >
|
||||
<button mat-menu-item translate (click)=setLineNumberingMode(0)>none</button>
|
||||
<button mat-menu-item translate (click)=setLineNumberingMode(1)>inline</button>
|
||||
<button mat-menu-item translate (click)=setLineNumberingMode(2)>outside</button>
|
||||
<mat-menu #lineNumberingMenu="matMenu">
|
||||
<div *ngIf="motion">
|
||||
<button mat-menu-item translate (click)=setLineNumberingMode(0) [ngClass]="{ 'selected': motion.lnMode === 0 }">none</button>
|
||||
<button mat-menu-item translate (click)=setLineNumberingMode(1) [ngClass]="{ 'selected': motion.lnMode === 1 }">inline</button>
|
||||
<button mat-menu-item translate (click)=setLineNumberingMode(2) [ngClass]="{ 'selected': motion.lnMode === 2 }">outside</button>
|
||||
</div>
|
||||
</mat-menu>
|
||||
|
||||
<!-- Diff View Menu -->
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { MatDialog, MatExpansionPanel, MatSnackBar, MatSelectChange, MatCheckboxChange } from '@angular/material';
|
||||
import { MatDialog, MatExpansionPanel, MatSnackBar, MatCheckboxChange } from '@angular/material';
|
||||
|
||||
import { Category } from '../../../../shared/models/motions/category';
|
||||
import { ViewportService } from '../../../../core/services/viewport.service';
|
||||
@ -28,6 +28,7 @@ import { StatuteParagraphRepositoryService } from '../../services/statute-paragr
|
||||
import { ConfigService } from '../../../../core/services/config.service';
|
||||
import { Workflow } from 'app/shared/models/motions/workflow';
|
||||
import { take, takeWhile, multicast, skipWhile } from 'rxjs/operators';
|
||||
import { LocalPermissionsService } from '../../services/local-permissions.service';
|
||||
|
||||
/**
|
||||
* Component for the motion detail view
|
||||
@ -94,11 +95,22 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
||||
private _motion: ViewMotion;
|
||||
|
||||
/**
|
||||
* Value of the configuration variable `motions_statutes_enabled` - are statutes enabled?
|
||||
* Value of the config variable `motions_statutes_enabled` - are statutes enabled?
|
||||
* @TODO replace by direct access to config variable, once it's available from the templates
|
||||
*/
|
||||
public statutesEnabled: boolean;
|
||||
|
||||
/**
|
||||
* Value of the config variable `motions_min_supporters`
|
||||
*/
|
||||
public minSupporters: number;
|
||||
|
||||
/**
|
||||
* Value of the config variable `motions_preamble`
|
||||
*/
|
||||
public preamble: string;
|
||||
|
||||
|
||||
/**
|
||||
* Copy of the motion that the user might edit
|
||||
*/
|
||||
@ -154,6 +166,11 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
||||
*/
|
||||
public supporterObserver: BehaviorSubject<User[]>;
|
||||
|
||||
/**
|
||||
* Determine if the name of supporters are visible
|
||||
*/
|
||||
public showSupporters = false;
|
||||
|
||||
/**
|
||||
* Value for os-motion-detail-diff: when this is set, that component scrolls to the given change
|
||||
*/
|
||||
@ -193,6 +210,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
||||
translate: TranslateService,
|
||||
matSnackBar: MatSnackBar,
|
||||
public vp: ViewportService,
|
||||
public perms: LocalPermissionsService,
|
||||
private op: OperatorService,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
@ -226,11 +244,22 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
||||
this.workflowObserver.next(DS.getAll(Workflow));
|
||||
}
|
||||
});
|
||||
// load config variables
|
||||
this.configService.get('motions_statutes_enabled').subscribe(
|
||||
(enabled: boolean): void => {
|
||||
this.statutesEnabled = enabled;
|
||||
}
|
||||
);
|
||||
this.configService.get('motions_min_supporters').subscribe(
|
||||
(supporters: number): void => {
|
||||
this.minSupporters = supporters;
|
||||
}
|
||||
);
|
||||
this.configService.get('motions_preamble').subscribe(
|
||||
(preamble: string): void => {
|
||||
this.preamble = preamble;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -343,7 +372,6 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
||||
* The AutoUpdate-Service should see a change once it arrives and show it
|
||||
* in the list view automatically
|
||||
*
|
||||
* TODO: state is not yet saved. Need a special "put" command. Repo should handle this.
|
||||
*/
|
||||
public async saveMotion(): Promise<void> {
|
||||
const newMotionValues = { ...this.metaInfoForm.value, ...this.contentForm.value };
|
||||
@ -410,7 +438,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
||||
|
||||
/**
|
||||
* Sets the motions line numbering mode
|
||||
* @param mode Needs to fot to the enum defined in ViewMotion
|
||||
* @param mode Needs to got the enum defined in ViewMotion
|
||||
*/
|
||||
public setLineNumberingMode(mode: LineNumberingMode): void {
|
||||
this.motion.lnMode = mode;
|
||||
@ -578,19 +606,49 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
||||
}
|
||||
|
||||
/**
|
||||
* Executed after selecting a state
|
||||
* @param selection MatSelectChange that contains the workflow id
|
||||
* Supports the motion (as requested user)
|
||||
*/
|
||||
public onChangeState(selection: MatSelectChange): void {
|
||||
this.repo.setState(this.motion, selection.value);
|
||||
public support(): void {
|
||||
this.repo.support(this.motion).then(null, this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executed after selecting the recommenders state
|
||||
* @param selection MatSelectChange that contains the workflow id
|
||||
* Unsupports the motion
|
||||
*/
|
||||
public onChangerRecommenderState(selection: MatSelectChange): void {
|
||||
this.repo.setRecommenderState(this.motion, selection.value);
|
||||
public unsupport(): void {
|
||||
this.repo.unsupport(this.motion).then(null, this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the dialog with all supporters.
|
||||
* TODO: open dialog here!
|
||||
*/
|
||||
public openSupportersDialog(): void {
|
||||
this.showSupporters = !this.showSupporters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state
|
||||
* @param id Motion state id
|
||||
*/
|
||||
public setState(id: number): void {
|
||||
this.repo.setState(this.motion, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the recommendation
|
||||
* @param id Motion recommendation id
|
||||
*/
|
||||
public setRecommendation(id: number): void {
|
||||
this.repo.setRecommendation(this.motion, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the category for current motion
|
||||
* @param id Motion category id
|
||||
*/
|
||||
public setCategory(id: number): void {
|
||||
this.repo.setCatetory(this.motion, id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,6 +65,20 @@
|
||||
<span translate>by</span>
|
||||
{{ motion.submitters }}
|
||||
</span>
|
||||
<br>
|
||||
<!-- state -->
|
||||
<mat-basic-chip [ngClass]="{
|
||||
'green': motion.state.css_class === 'success',
|
||||
'red': motion.state.css_class === 'danger',
|
||||
'grey': motion.state.css_class === 'default',
|
||||
'lightblue': motion.state.css_class === 'primary' }">
|
||||
{{ motion.state.name | translate }}
|
||||
</mat-basic-chip>
|
||||
|
||||
<!-- recommendation -->
|
||||
<span *ngIf="motion.recommendation" >
|
||||
<mat-basic-chip class="bluegrey">{{ motion.recommendation.recommendation_label | translate }}</mat-basic-chip>
|
||||
</span>
|
||||
</div>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
@ -72,13 +86,10 @@
|
||||
<!-- state column -->
|
||||
<ng-container matColumnDef="state">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header>State</mat-header-cell>
|
||||
<mat-cell *matCellDef="let motion">
|
||||
<!--div *ngIf='isDisplayIcon(motion.state) && motion.state' class='innerTable'>
|
||||
<mat-icon>{{ getStateIcon(motion.state) }}</mat-icon>
|
||||
</div>-->
|
||||
<mat-chip-list>
|
||||
<mat-chip color="accent">{{ motion.state }}</mat-chip>
|
||||
</mat-chip-list>
|
||||
<mat-cell *matCellDef="let motion" (click)="selectMotion(motion)">
|
||||
<div *ngIf='motion.category' class='small'>
|
||||
<mat-icon>device_hub</mat-icon> {{ motion.category }}
|
||||
</div>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
@ -98,7 +109,7 @@
|
||||
|
||||
<mat-header-row *matHeaderRowDef="getColumnDefinition()"></mat-header-row>
|
||||
<mat-row [ngClass]="selectedRows.indexOf(row) >= 0 ? 'selected': ''" (click)='selectItem(row, $event)'
|
||||
*matRowDef="let row; columns: getColumnDefinition()">
|
||||
*matRowDef="let row; columns: getColumnDefinition()" class="lg">
|
||||
</mat-row>
|
||||
</mat-table>
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
/** css hacks https://codepen.io/edge0703/pen/iHJuA */
|
||||
.innerTable {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
line-height: normal;
|
||||
vertical-align: top;
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
.os-listview-table {
|
||||
@ -23,10 +23,10 @@
|
||||
|
||||
.motion-list-title {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.motion-list-from {
|
||||
margin-top: 5px;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
font-size: 90%;
|
||||
}
|
||||
|
@ -227,6 +227,11 @@ export class ViewMotion extends BaseViewModel {
|
||||
this._block = block;
|
||||
|
||||
// TODO: Should be set using a a config variable
|
||||
/*this._configService.get('motions_default_line_numbering').subscribe(
|
||||
(mode: string): void => {
|
||||
this.lnMode = LineNumberingMode.Outside;
|
||||
}
|
||||
);*/
|
||||
this.lnMode = LineNumberingMode.Outside;
|
||||
this.crMode = ChangeRecoMode.Original;
|
||||
this.lineLength = 80;
|
||||
@ -265,9 +270,9 @@ export class ViewMotion extends BaseViewModel {
|
||||
this.updateItem(update as Item);
|
||||
} else if (update instanceof MotionBlock) {
|
||||
this.updateMotionBlock(update);
|
||||
} else if (update instanceof User) {
|
||||
this.updateUser(update as User);
|
||||
}
|
||||
// TODO: There is no way (yet) to add Submitters to a motion
|
||||
// Thus, this feature could not be tested
|
||||
}
|
||||
|
||||
/**
|
||||
@ -310,6 +315,23 @@ export class ViewMotion extends BaseViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update routine for the agenda Item
|
||||
* @param update potentially the changed agenda Item. Needs manual verification
|
||||
*/
|
||||
public updateUser(update: User): void {
|
||||
if (this.motion) {
|
||||
if (this.motion.submitters && this.motion.submitters.findIndex(user => user.user_id === update.id)) {
|
||||
const userIndex = this.submitters.findIndex(user => user.id === update.id);
|
||||
this.submitters[userIndex] = update as User;
|
||||
}
|
||||
if (this.motion.supporters_id && this.motion.supporters_id.includes(update.id)) {
|
||||
const userIndex = this.supporters.findIndex(user => user.id === update.id);
|
||||
this.supporters[userIndex] = update as User;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public hasSupporters(): boolean {
|
||||
return !!(this.supporters && this.supporters.length > 0);
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LocalPermissionsService } from './local-permissions.service';
|
||||
import { E2EImportsModule } from '../../../../e2e-imports.module';
|
||||
|
||||
describe('LocalPermissionsService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({ imports: [E2EImportsModule] }));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: LocalPermissionsService = TestBed.get(LocalPermissionsService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,55 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { OperatorService } from '../../../core/services/operator.service';
|
||||
import { ViewMotion } from '../models/view-motion';
|
||||
import { ConfigService } from '../../../core/services/config.service';
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class LocalPermissionsService {
|
||||
|
||||
public configMinSupporters: number;
|
||||
|
||||
public constructor(
|
||||
private operator: OperatorService,
|
||||
private configService: ConfigService,
|
||||
) {
|
||||
// load config variables
|
||||
this.configService.get('motions_min_supporters').subscribe(
|
||||
(supporters: number): void => {
|
||||
this.configMinSupporters = supporters;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should determine if the user (Operator) has the
|
||||
* correct permission to perform the given action.
|
||||
*
|
||||
* actions might be:
|
||||
* - support
|
||||
*
|
||||
* @param action the action the user tries to perform
|
||||
*/
|
||||
public isAllowed(action: string, motion?: ViewMotion): boolean {
|
||||
if (motion) {
|
||||
switch (action) {
|
||||
case 'support':
|
||||
return (
|
||||
this.operator.hasPerms('motions.can_support') &&
|
||||
this.configMinSupporters > 0 &&
|
||||
motion.state.allow_support &&
|
||||
(motion.submitters.indexOf(this.operator.user) === -1) &&
|
||||
(motion.supporters.indexOf(this.operator.user) === -1));
|
||||
case 'unsupport':
|
||||
return (
|
||||
motion.state.allow_support &&
|
||||
(motion.supporters.indexOf(this.operator.user) !== -1)
|
||||
);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -106,7 +106,7 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
||||
* Creates a (real) motion with patched data and delegate it
|
||||
* to the {@link DataSendService}
|
||||
*
|
||||
* @param update the form data containing the update values
|
||||
* @param update the form data containing the updated values
|
||||
* @param viewMotion The View Motion. If not present, a new motion will be created
|
||||
* TODO: Remove the viewMotion and make it actually distignuishable from save()
|
||||
*/
|
||||
@ -123,7 +123,7 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
||||
* Creates a (real) motion with patched data and delegate it
|
||||
* to the {@link DataSendService}
|
||||
*
|
||||
* @param update the form data containing the update values
|
||||
* @param update the form data containing the updated values
|
||||
* @param viewMotion The View Motion. If not present, a new motion will be created
|
||||
*/
|
||||
public async update(update: Partial<Motion>, viewMotion: ViewMotion): Promise<void> {
|
||||
@ -158,11 +158,23 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
||||
* Set the recommenders state of a motion
|
||||
*
|
||||
* @param viewMotion target motion
|
||||
* @param stateId the number that indicates the state
|
||||
* @param recommendationId the number that indicates the recommendation
|
||||
*/
|
||||
public async setRecommenderState(viewMotion: ViewMotion, stateId: number): Promise<void> {
|
||||
public async setRecommendation(viewMotion: ViewMotion, recommendationId: number): Promise<void> {
|
||||
const restPath = `/rest/motions/motion/${viewMotion.id}/set_recommendation/`;
|
||||
await this.httpService.put(restPath, { recommendation: stateId });
|
||||
await this.httpService.put(restPath, { recommendation: recommendationId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the category of a motion
|
||||
*
|
||||
* @param viewMotion target motion
|
||||
* @param categoryId the number that indicates the category
|
||||
*/
|
||||
public async setCatetory(viewMotion: ViewMotion, categoryId: number): Promise<void> {
|
||||
const motion = viewMotion.motion;
|
||||
motion.category_id = categoryId;
|
||||
await this.update(motion, viewMotion);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,6 +187,26 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
||||
await this.httpService.post(url, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports the motion
|
||||
*
|
||||
* @param viewMotion target motion
|
||||
*/
|
||||
public async support(viewMotion: ViewMotion): Promise<void> {
|
||||
const url = `/rest/motions/motion/${viewMotion.id}/support/`;
|
||||
await this.httpService.post(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsupports the motion
|
||||
*
|
||||
* @param viewMotion target motion
|
||||
*/
|
||||
public async unsupport(viewMotion: ViewMotion): Promise<void> {
|
||||
const url = `/rest/motions/motion/${viewMotion.id}/support/`;
|
||||
await this.httpService.delete(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the motion text using the line numbering and change
|
||||
* reco algorithm.
|
||||
|
@ -41,6 +41,10 @@ body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.generic-mini-button {
|
||||
bottom: -28px;
|
||||
z-index: 100;
|
||||
@ -114,6 +118,9 @@ body {
|
||||
cursor: pointer;
|
||||
background-color: rgba(0, 0, 0, 0.055);
|
||||
}
|
||||
mat-row.lg {
|
||||
height: 90px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-plus-distance {
|
||||
@ -157,7 +164,6 @@ mat-panel-title mat-icon {
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
|
||||
.hidden-cell {
|
||||
flex: 0;
|
||||
width: 0;
|
||||
@ -188,3 +194,59 @@ mat-panel-title mat-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-chip,
|
||||
.mat-basic-chip {
|
||||
font-size: 12px;
|
||||
min-height: 22px !important;
|
||||
border-radius: 5px !important;
|
||||
padding: 4px 8px !important;
|
||||
margin: 8px 8px 8px 0;
|
||||
}
|
||||
|
||||
.mat-chip:focus,
|
||||
.mat-basic-chip:focus {
|
||||
outline: none;
|
||||
}
|
||||
button.mat-menu-item.selected {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
/** Colors **/
|
||||
.lightblue {
|
||||
background-color: rgb(33, 150, 243) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.darkblue {
|
||||
background-color: rgb(63, 81, 181) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.green,
|
||||
.success {
|
||||
background-color: rgb(76, 175, 80) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.red,
|
||||
.error {
|
||||
background-color: rgb(255, 82, 82) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.yellow,
|
||||
.warning {
|
||||
background-color: rgb(255, 193, 7) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.bluegrey {
|
||||
background-color: rgb(96, 125, 139) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.grey {
|
||||
background-color: #e0e0e0 !important;
|
||||
color: rgba(0, 0, 0, 0.87) !important;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user