Add Tags into motion details
Adds tags to motion repository, view-motion and selectable for the motion detail view
This commit is contained in:
parent
f3c3d8ab8c
commit
f992b77d99
@ -31,6 +31,7 @@ import { ViewUnifiedChange } from '../../../site/motions/models/view-unified-cha
|
||||
import { ViewStatuteParagraph } from '../../../site/motions/models/view-statute-paragraph';
|
||||
import { Workflow } from '../../../shared/models/motions/workflow';
|
||||
import { WorkflowState } from '../../../shared/models/motions/workflow-state';
|
||||
import { Tag } from 'app/shared/models/core/tag';
|
||||
|
||||
/**
|
||||
* Repository Services for motions (and potentially categories)
|
||||
@ -71,7 +72,7 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
||||
private personalNoteService: PersonalNoteService,
|
||||
private translate: TranslateService
|
||||
) {
|
||||
super(DS, mapperService, Motion, [Category, User, Workflow, Item, MotionBlock, Mediafile]);
|
||||
super(DS, mapperService, Motion, [Category, User, Workflow, Item, MotionBlock, Mediafile, Tag]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,11 +91,23 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
||||
const item = this.DS.get(Item, motion.agenda_item_id);
|
||||
const block = this.DS.get(MotionBlock, motion.motion_block_id);
|
||||
const attachments = this.DS.getMany(Mediafile, motion.attachments_id);
|
||||
const tags = this.DS.getMany(Tag, motion.tags_id);
|
||||
let state: WorkflowState = null;
|
||||
if (workflow) {
|
||||
state = workflow.getStateById(motion.state_id);
|
||||
}
|
||||
return new ViewMotion(motion, category, submitters, supporters, workflow, state, item, block, attachments);
|
||||
return new ViewMotion(
|
||||
motion,
|
||||
category,
|
||||
submitters,
|
||||
supporters,
|
||||
workflow,
|
||||
state,
|
||||
item,
|
||||
block,
|
||||
attachments,
|
||||
tags
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -219,6 +232,26 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
||||
await this.update(motion, viewMotion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new or removes existing tags from motions
|
||||
*
|
||||
* @param viewMotion the motion to tag
|
||||
* @param tagId the tags id to add or remove
|
||||
*/
|
||||
public async setTag(viewMotion: ViewMotion, tagId: number): Promise<void> {
|
||||
const motion = viewMotion.motion;
|
||||
const tagIndex = motion.tags_id.findIndex(tag => tag === tagId);
|
||||
|
||||
if (tagIndex === -1) {
|
||||
// add tag to motion
|
||||
motion.tags_id.push(tagId);
|
||||
} else {
|
||||
// remove tag from motion
|
||||
motion.tags_id.splice(tagIndex, 1);
|
||||
}
|
||||
await this.update(motion, viewMotion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the submitters by sending a request to the server,
|
||||
*
|
||||
|
@ -303,6 +303,47 @@
|
||||
</mat-basic-chip>
|
||||
</div>
|
||||
|
||||
<!-- Tags -->
|
||||
<!-- Disabled during "new motion" since changing has no effect -->
|
||||
<div *ngIf="!editMotion && tagObserver.value.length > 0">
|
||||
<h4 *ngIf="perms.isAllowed('change_metadata', motion) || motion.hasTags()">Tags</h4>
|
||||
|
||||
<!-- For privileged users -->
|
||||
<div *ngIf="perms.isAllowed('change_metadata', motion)">
|
||||
<!-- Selection menu -->
|
||||
<mat-menu #tagMenu="matMenu">
|
||||
<button mat-menu-item *ngFor="let tag of tagObserver.value" (click)="setTag($event, tag.id)">
|
||||
<mat-icon *ngIf="motion.tags.includes(tag)">check</mat-icon>
|
||||
{{ tag }}
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
||||
<!-- Make the whole container a trigger to prevent unexpected menu behavior -->
|
||||
<div [matMenuTriggerFor]="tagMenu">
|
||||
<!-- No selected tags -->
|
||||
<mat-basic-chip *ngIf="!motion.hasTags()" class="grey" disabled>
|
||||
{{ '–' }}
|
||||
</mat-basic-chip>
|
||||
|
||||
<!-- Display a chip list of tags -->
|
||||
<mat-chip-list class="mat-chip-list-stacked">
|
||||
<mat-basic-chip *ngFor="let tag of motion.tags" class="grey" disabled>
|
||||
{{ tag }}
|
||||
</mat-basic-chip>
|
||||
</mat-chip-list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- For non privileged users -->
|
||||
<div *ngIf="!perms.isAllowed('change_metadata', motion)">
|
||||
<mat-chip-list class="mat-chip-list-stacked">
|
||||
<mat-basic-chip *ngFor="let tag of motion.tags" class="grey">
|
||||
{{ tag }}
|
||||
</mat-basic-chip>
|
||||
</mat-chip-list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Block -->
|
||||
<div *ngIf="!editMotion && blockObserver.value.length > 0">
|
||||
<h4 translate>Motion block</h4>
|
||||
@ -494,8 +535,13 @@
|
||||
|
||||
<!-- The HTML Editor -->
|
||||
<editor formControlName="text" [init]="tinyMceSettings" *ngIf="motion && editMotion" required></editor>
|
||||
<div *ngIf="contentForm.get('text').invalid && (contentForm.get('text').dirty || contentForm.get('text').touched)"
|
||||
class="red-warning-text" translate>
|
||||
<div
|
||||
*ngIf="
|
||||
contentForm.get('text').invalid && (contentForm.get('text').dirty || contentForm.get('text').touched)
|
||||
"
|
||||
class="red-warning-text"
|
||||
translate
|
||||
>
|
||||
This field is required.
|
||||
</div>
|
||||
|
||||
@ -506,15 +552,30 @@
|
||||
|
||||
<!-- Reason -->
|
||||
<div *ngIf="motion.reason || editMotion">
|
||||
<h3 [ngClass]="(reasonRequired && contentForm.get('reason').invalid && (contentForm.get('reason').dirty || contentForm.get('reason').touched)) ? 'red-warning-text' : ''">
|
||||
<h3
|
||||
[ngClass]="
|
||||
reasonRequired &&
|
||||
contentForm.get('reason').invalid &&
|
||||
(contentForm.get('reason').dirty || contentForm.get('reason').touched)
|
||||
? 'red-warning-text'
|
||||
: ''
|
||||
"
|
||||
>
|
||||
<span translate>Reason</span> <span *ngIf="reasonRequired && editMotion">*</span>
|
||||
</h3>
|
||||
<div class="motion-text" *ngIf="!editMotion"><div [innerHtml]="motion.reason"></div></div>
|
||||
|
||||
<!-- The HTML Editor -->
|
||||
<editor formControlName="reason" [init]="tinyMceSettings" *ngIf="editMotion" required></editor>
|
||||
<div *ngIf="reasonRequired && contentForm.get('reason').invalid && (contentForm.get('reason').dirty || contentForm.get('reason').touched)"
|
||||
class="red-warning-text" translate>
|
||||
<div
|
||||
*ngIf="
|
||||
reasonRequired &&
|
||||
contentForm.get('reason').invalid &&
|
||||
(contentForm.get('reason').dirty || contentForm.get('reason').touched)
|
||||
"
|
||||
class="red-warning-text"
|
||||
translate
|
||||
>
|
||||
This field is required.
|
||||
</div>
|
||||
</div>
|
||||
|
@ -275,3 +275,9 @@ span {
|
||||
padding: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-chip-list-stacked {
|
||||
.mat-chip {
|
||||
margin: 4px 4px 4px 8px;
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ import { ViewUnifiedChange } from '../../models/view-unified-change';
|
||||
import { ViewStatuteParagraph } from '../../models/view-statute-paragraph';
|
||||
import { Workflow } from 'app/shared/models/motions/workflow';
|
||||
import { LinenumberingService } from 'app/core/ui-services/linenumbering.service';
|
||||
import { Tag } from 'app/shared/models/core/tag';
|
||||
|
||||
/**
|
||||
* Component for the motion detail view
|
||||
@ -221,6 +222,11 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
||||
*/
|
||||
public agendaItemObserver: BehaviorSubject<Item[]>;
|
||||
|
||||
/**
|
||||
* Subject for tags
|
||||
*/
|
||||
public tagObserver: BehaviorSubject<Tag[]>;
|
||||
|
||||
/**
|
||||
* Determine if the name of supporters are visible
|
||||
*/
|
||||
@ -356,6 +362,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
||||
this.blockObserver = new BehaviorSubject(DS.getAll(MotionBlock));
|
||||
this.mediafilesObserver = new BehaviorSubject(DS.getAll(Mediafile));
|
||||
this.agendaItemObserver = new BehaviorSubject(DS.getAll(Item));
|
||||
this.tagObserver = new BehaviorSubject(DS.getAll(Tag));
|
||||
|
||||
// Make sure the subjects are updated, when a new Model for the type arrives
|
||||
this.DS.changeObservable.subscribe(newModel => {
|
||||
@ -372,6 +379,8 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
||||
this.mediafilesObserver.next(DS.getAll(Mediafile));
|
||||
} else if (newModel instanceof Item) {
|
||||
this.agendaItemObserver.next(DS.getAll(Item));
|
||||
} else if (newModel instanceof Tag) {
|
||||
this.tagObserver.next(DS.getAll(Tag));
|
||||
}
|
||||
});
|
||||
|
||||
@ -1013,6 +1022,17 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or removes a tag to the current motion
|
||||
*
|
||||
* @param id Motion tag id
|
||||
*/
|
||||
public setTag(event: MouseEvent, id: number): void {
|
||||
console.log('event: ', event);
|
||||
event.stopPropagation();
|
||||
this.repo.setTag(this.motion, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the current motion to a motion block
|
||||
*
|
||||
|
@ -12,6 +12,7 @@ import { ViewMotionCommentSection } from './view-motion-comment-section';
|
||||
import { Workflow } from '../../../shared/models/motions/workflow';
|
||||
import { WorkflowState } from '../../../shared/models/motions/workflow-state';
|
||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||
import { Tag } from 'app/shared/models/core/tag';
|
||||
|
||||
/**
|
||||
* The line numbering mode for the motion detail view.
|
||||
@ -51,6 +52,7 @@ export class ViewMotion extends BaseProjectableModel {
|
||||
protected _item: Item;
|
||||
protected _block: MotionBlock;
|
||||
protected _attachments: Mediafile[];
|
||||
protected _tags: Tag[];
|
||||
public personalNote: PersonalNoteContent;
|
||||
|
||||
/**
|
||||
@ -232,6 +234,10 @@ export class ViewMotion extends BaseProjectableModel {
|
||||
return this._attachments ? this._attachments : null;
|
||||
}
|
||||
|
||||
public get tags(): Tag[] {
|
||||
return this._tags ? this._tags : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns the creation date as Date object
|
||||
*/
|
||||
@ -313,7 +319,8 @@ export class ViewMotion extends BaseProjectableModel {
|
||||
state?: WorkflowState,
|
||||
item?: Item,
|
||||
block?: MotionBlock,
|
||||
attachments?: Mediafile[]
|
||||
attachments?: Mediafile[],
|
||||
tags?: Tag[]
|
||||
) {
|
||||
super();
|
||||
this._motion = motion;
|
||||
@ -325,6 +332,7 @@ export class ViewMotion extends BaseProjectableModel {
|
||||
this._item = item;
|
||||
this._block = block;
|
||||
this._attachments = attachments;
|
||||
this._tags = tags;
|
||||
}
|
||||
|
||||
public getTitle(): string {
|
||||
@ -364,6 +372,8 @@ export class ViewMotion extends BaseProjectableModel {
|
||||
this.updateUser(update as User);
|
||||
} else if (update instanceof Mediafile) {
|
||||
this.updateAttachments(update as Mediafile);
|
||||
} else if (update instanceof Tag) {
|
||||
this.updateTags(update as Tag);
|
||||
}
|
||||
}
|
||||
|
||||
@ -381,7 +391,7 @@ export class ViewMotion extends BaseProjectableModel {
|
||||
/**
|
||||
* Update routine for the workflow
|
||||
*
|
||||
* @param workflow potentially the changed workflow (state). Needs manual verification
|
||||
* @param workflow potentially the (changed workflow (state). Needs manual verification
|
||||
*/
|
||||
public updateWorkflow(workflow: Workflow): void {
|
||||
if (this.motion && workflow.id === this.motion.workflow_id) {
|
||||
@ -443,6 +453,15 @@ export class ViewMotion extends BaseProjectableModel {
|
||||
}
|
||||
}
|
||||
|
||||
public updateTags(update: Tag): void {
|
||||
if (this.motion) {
|
||||
if (this.tags_id && this.tags_id.includes(update.id)) {
|
||||
const tagIndex = this.tags.findIndex(tag => tag.id === update.id);
|
||||
this.tags[tagIndex] = update as Tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public hasSupporters(): boolean {
|
||||
return !!(this.supporters && this.supporters.length > 0);
|
||||
}
|
||||
@ -451,6 +470,10 @@ export class ViewMotion extends BaseProjectableModel {
|
||||
return !!(this.attachments && this.attachments.length > 0);
|
||||
}
|
||||
|
||||
public hasTags(): boolean {
|
||||
return !!(this.tags && this.tags.length > 0);
|
||||
}
|
||||
|
||||
public isStatuteAmendment(): boolean {
|
||||
return !!this.statute_paragraph_id;
|
||||
}
|
||||
@ -508,7 +531,8 @@ export class ViewMotion extends BaseProjectableModel {
|
||||
this._state,
|
||||
this._item,
|
||||
this._block,
|
||||
this._attachments
|
||||
this._attachments,
|
||||
this._tags
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user