Merge pull request #3887 from jsaalfeld/categories
implementing categories
This commit is contained in:
commit
781a2dcdf8
8
client/package-lock.json
generated
8
client/package-lock.json
generated
@ -4779,13 +4779,15 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
@ -4808,7 +4810,8 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"console-control-strings": {
|
"console-control-strings": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
@ -4975,6 +4978,7 @@
|
|||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,7 @@ export class DataStoreService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private getCollectionString<T extends BaseModel>(collectionType: ModelConstructor<T> | string): string {
|
private getCollectionString<T extends BaseModel<T>>(collectionType: ModelConstructor<T> | string): string {
|
||||||
if (typeof collectionType === 'string') {
|
if (typeof collectionType === 'string') {
|
||||||
return collectionType;
|
return collectionType;
|
||||||
} else {
|
} else {
|
||||||
@ -199,7 +199,7 @@ export class DataStoreService {
|
|||||||
* @example: this.DS.get(User, 1)
|
* @example: this.DS.get(User, 1)
|
||||||
* @example: this.DS.get<Countdown>('core/countdown', 2)
|
* @example: this.DS.get<Countdown>('core/countdown', 2)
|
||||||
*/
|
*/
|
||||||
public get<T extends BaseModel>(collectionType: ModelConstructor<T> | string, id: number): T {
|
public get<T extends BaseModel<T>>(collectionType: ModelConstructor<T> | string, id: number): T {
|
||||||
const collectionString = this.getCollectionString<T>(collectionType);
|
const collectionString = this.getCollectionString<T>(collectionType);
|
||||||
|
|
||||||
const collection: ModelCollection = this.modelStore[collectionString];
|
const collection: ModelCollection = this.modelStore[collectionString];
|
||||||
@ -219,7 +219,7 @@ export class DataStoreService {
|
|||||||
* @example: this.DS.getMany(User, [1,2,3,4,5])
|
* @example: this.DS.getMany(User, [1,2,3,4,5])
|
||||||
* @example: this.DS.getMany<User>('users/user', [1,2,3,4,5])
|
* @example: this.DS.getMany<User>('users/user', [1,2,3,4,5])
|
||||||
*/
|
*/
|
||||||
public getMany<T extends BaseModel>(collectionType: ModelConstructor<T> | string, ids: number[]): T[] {
|
public getMany<T extends BaseModel<T>>(collectionType: ModelConstructor<T> | string, ids: number[]): T[] {
|
||||||
const collectionString = this.getCollectionString<T>(collectionType);
|
const collectionString = this.getCollectionString<T>(collectionType);
|
||||||
|
|
||||||
const collection: ModelCollection = this.modelStore[collectionString];
|
const collection: ModelCollection = this.modelStore[collectionString];
|
||||||
@ -242,7 +242,7 @@ export class DataStoreService {
|
|||||||
* @example: this.DS.getAll(User)
|
* @example: this.DS.getAll(User)
|
||||||
* @example: this.DS.getAll<User>('users/user')
|
* @example: this.DS.getAll<User>('users/user')
|
||||||
*/
|
*/
|
||||||
public getAll<T extends BaseModel>(collectionType: ModelConstructor<T> | string): T[] {
|
public getAll<T extends BaseModel<T>>(collectionType: ModelConstructor<T> | string): T[] {
|
||||||
const collectionString = this.getCollectionString<T>(collectionType);
|
const collectionString = this.getCollectionString<T>(collectionType);
|
||||||
|
|
||||||
const collection: ModelCollection = this.modelStore[collectionString];
|
const collection: ModelCollection = this.modelStore[collectionString];
|
||||||
@ -261,7 +261,7 @@ export class DataStoreService {
|
|||||||
* @return The BaseModel-list corresponding to the filter function
|
* @return The BaseModel-list corresponding to the filter function
|
||||||
* @example this.DS.filter<User>(User, myUser => myUser.first_name === "Max")
|
* @example this.DS.filter<User>(User, myUser => myUser.first_name === "Max")
|
||||||
*/
|
*/
|
||||||
public filter<T extends BaseModel>(
|
public filter<T extends BaseModel<T>>(
|
||||||
collectionType: ModelConstructor<T> | string,
|
collectionType: ModelConstructor<T> | string,
|
||||||
callback: (model: T) => boolean
|
callback: (model: T) => boolean
|
||||||
): T[] {
|
): T[] {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<mat-toolbar color='primary'>
|
<mat-toolbar color='primary'>
|
||||||
<button *ngIf="plusButton" class='generic-plus-button on-transition-fade' (click)=clickPlusButton() mat-fab>
|
<button *ngIf="plusButton" class='head-button on-transition-fade' (click)=clickPlusButton()
|
||||||
|
mat-fab>
|
||||||
<fa-icon icon='plus'></fa-icon>
|
<fa-icon icon='plus'></fa-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
.head-button {
|
||||||
|
bottom: -30px;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
@ -16,7 +16,7 @@ import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
|||||||
* ```html
|
* ```html
|
||||||
* <os-head-bar
|
* <os-head-bar
|
||||||
* appName="Files"
|
* appName="Files"
|
||||||
* PlusButton=true
|
* plusButton=true
|
||||||
* [menuList]=myMenu
|
* [menuList]=myMenu
|
||||||
* (plusButtonClicked)=onPlusButton()
|
* (plusButtonClicked)=onPlusButton()
|
||||||
* (ellipsisMenuItem)=onEllipsisItem($event)>
|
* (ellipsisMenuItem)=onEllipsisItem($event)>
|
||||||
@ -54,29 +54,34 @@ export class HeadBarComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* Input declaration for the app name
|
* Input declaration for the app name
|
||||||
*/
|
*/
|
||||||
@Input() public appName: string;
|
@Input()
|
||||||
|
public appName: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if there should be a plus button.
|
* Determine if there should be a plus button.
|
||||||
*/
|
*/
|
||||||
@Input() public plusButton: false;
|
@Input()
|
||||||
|
public plusButton: false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If not empty shows a ellipsis menu on the right side
|
* If not empty shows a ellipsis menu on the right side
|
||||||
*
|
*
|
||||||
* The parent needs to provide a menu, i.e `[menuList]=myMenu`.
|
* The parent needs to provide a menu, i.e `[menuList]=myMenu`.
|
||||||
*/
|
*/
|
||||||
@Input() public menuList: any[];
|
@Input()
|
||||||
|
public menuList: any[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit a signal to the parent component if the plus button was clicked
|
* Emit a signal to the parent component if the plus button was clicked
|
||||||
*/
|
*/
|
||||||
@Output() public plusButtonClicked = new EventEmitter<boolean>();
|
@Output()
|
||||||
|
public plusButtonClicked = new EventEmitter<boolean>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit a signal to the parent of an item in the menuList was selected.
|
* Emit a signal to the parent of an item in the menuList was selected.
|
||||||
*/
|
*/
|
||||||
@Output() public ellipsisMenuItem = new EventEmitter<any>();
|
@Output()
|
||||||
|
public ellipsisMenuItem = new EventEmitter<any>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Empty constructor
|
* Empty constructor
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
<mat-form-field [formGroup]="form">
|
<mat-form-field [formGroup]="form">
|
||||||
<mat-select [formControl]="formControl" placeholder="{{listname}}" multiple="{{multiple}}" #thisSelector>
|
<mat-select [formControl]="formControl" [placeholder]="listname" [multiple]="multiple" #thisSelector>
|
||||||
<ngx-mat-select-search [formControl]="filterControl"></ngx-mat-select-search>
|
<ngx-mat-select-search [formControl]="filterControl"></ngx-mat-select-search>
|
||||||
<mat-option *ngFor="let selectedItem of filteredItems | async" [value]="selectedItem">
|
<div *ngIf="!multiple">
|
||||||
|
<mat-option [value]="null"><span translate>None</span></mat-option>
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
</div>
|
||||||
|
<mat-option *ngFor="let selectedItem of filteredItems | async" [value]="selectedItem.id">
|
||||||
{{selectedItem.getTitle(translate)}}
|
{{selectedItem.getTitle(translate)}}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import { Component, OnInit, Input, ViewChild } from '@angular/core';
|
import { Component, OnInit, Input, ViewChild } from '@angular/core';
|
||||||
import { FormControl, FormGroup } from '@angular/forms';
|
import { FormControl, FormGroup } from '@angular/forms';
|
||||||
import { ReplaySubject, Subject } from 'rxjs';
|
import { Subject, ReplaySubject, BehaviorSubject } from 'rxjs';
|
||||||
import { MatSelect } from '@angular/material';
|
import { MatSelect } from '@angular/material';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
import { Displayable } from '../../models/base/displayable';
|
import { Displayable } from '../../models/base/displayable';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { Identifiable } from '../../models/base/identifiable';
|
||||||
|
|
||||||
|
type Selectable = Displayable & Identifiable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reusable Searchable Value Selector
|
* Reusable Searchable Value Selector
|
||||||
@ -20,7 +23,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
* ```html
|
* ```html
|
||||||
* <os-search-value-selector
|
* <os-search-value-selector
|
||||||
* ngDefaultControl
|
* ngDefaultControl
|
||||||
* multiple="true"
|
* [multiple]="true"
|
||||||
* placeholder="Placeholder"
|
* placeholder="Placeholder"
|
||||||
* [InputListValues]="myListValues",
|
* [InputListValues]="myListValues",
|
||||||
* [form]="myform_name",
|
* [form]="myform_name",
|
||||||
@ -37,7 +40,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
})
|
})
|
||||||
export class SearchValueSelectorComponent implements OnInit {
|
export class SearchValueSelectorComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* ngModel variable - Depricated with Angular 7
|
* ngModel variable - Deprecated with Angular 7
|
||||||
* DO NOT USE: READ AT remove() FUNCTION!
|
* DO NOT USE: READ AT remove() FUNCTION!
|
||||||
*/
|
*/
|
||||||
public myModel = [];
|
public myModel = [];
|
||||||
@ -48,9 +51,9 @@ export class SearchValueSelectorComponent implements OnInit {
|
|||||||
public filterControl = new FormControl();
|
public filterControl = new FormControl();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of the filtered content, when entering somithing in the search bar
|
* List of the filtered content, when entering something in the search bar
|
||||||
*/
|
*/
|
||||||
public filteredItems: ReplaySubject<Displayable[]> = new ReplaySubject<Displayable[]>(1);
|
public filteredItems: ReplaySubject<Selectable[]> = new ReplaySubject<Selectable[]>(1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decide if this should be a single or multi-select-field
|
* Decide if this should be a single or multi-select-field
|
||||||
@ -62,7 +65,7 @@ export class SearchValueSelectorComponent implements OnInit {
|
|||||||
* The Input List Values
|
* The Input List Values
|
||||||
*/
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
public InputListValues: Displayable[];
|
public InputListValues: BehaviorSubject<Selectable[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Placeholder of the List
|
* Placeholder of the List
|
||||||
@ -111,13 +114,11 @@ export class SearchValueSelectorComponent implements OnInit {
|
|||||||
* onInit with filter ans subscription on filter
|
* onInit with filter ans subscription on filter
|
||||||
*/
|
*/
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
// load the initial item list
|
this.filteredItems.next(this.InputListValues.getValue());
|
||||||
this.filteredItems.next(this.InputListValues.slice());
|
|
||||||
// listen to value changes
|
// listen to value changes
|
||||||
this.filterControl.valueChanges.pipe(takeUntil(this._onDestroy)).subscribe(() => {
|
this.filterControl.valueChanges.pipe(takeUntil(this._onDestroy)).subscribe(() => {
|
||||||
this.filterItems();
|
this.filterItems();
|
||||||
});
|
});
|
||||||
// this.multiSelect.stateChanges.subscribe(fn => console.log('ive changed'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,14 +131,14 @@ export class SearchValueSelectorComponent implements OnInit {
|
|||||||
// get the search keyword
|
// get the search keyword
|
||||||
let search = this.filterControl.value;
|
let search = this.filterControl.value;
|
||||||
if (!search) {
|
if (!search) {
|
||||||
this.filteredItems.next(this.InputListValues.slice());
|
this.filteredItems.next(this.InputListValues.getValue());
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
search = search.toLowerCase();
|
search = search.toLowerCase();
|
||||||
}
|
}
|
||||||
// filter the values
|
// filter the values
|
||||||
this.filteredItems.next(
|
this.filteredItems.next(
|
||||||
this.InputListValues.filter(
|
this.InputListValues.getValue().filter(
|
||||||
selectedItem =>
|
selectedItem =>
|
||||||
selectedItem
|
selectedItem
|
||||||
.toString()
|
.toString()
|
||||||
@ -154,7 +155,7 @@ export class SearchValueSelectorComponent implements OnInit {
|
|||||||
* places, but can't reflect the changes in both places. Until this can be done this will be unused code
|
* places, but can't reflect the changes in both places. Until this can be done this will be unused code
|
||||||
* @param item the selected item to be removed
|
* @param item the selected item to be removed
|
||||||
*/
|
*/
|
||||||
public remove(item: Displayable): void {
|
public remove(item: Selectable): void {
|
||||||
const myArr = this.thisSelector.value;
|
const myArr = this.thisSelector.value;
|
||||||
const index = myArr.indexOf(item, 0);
|
const index = myArr.indexOf(item, 0);
|
||||||
// my model was the form according to fix
|
// my model was the form according to fix
|
||||||
|
@ -2,15 +2,18 @@ import { OpenSlidesComponent } from 'app/openslides.component';
|
|||||||
import { Deserializable } from './deserializable';
|
import { Deserializable } from './deserializable';
|
||||||
import { CollectionStringModelMapperService } from '../../../core/services/collectionStringModelMapper.service';
|
import { CollectionStringModelMapperService } from '../../../core/services/collectionStringModelMapper.service';
|
||||||
import { Displayable } from './displayable';
|
import { Displayable } from './displayable';
|
||||||
|
import { Identifiable } from './identifiable';
|
||||||
|
|
||||||
export interface ModelConstructor<T extends BaseModel> {
|
export interface ModelConstructor<T extends BaseModel<T>> {
|
||||||
new (...args: any[]): T;
|
new (...args: any[]): T;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract parent class to set rules and functions for all models.
|
* Abstract parent class to set rules and functions for all models.
|
||||||
|
* When inherit from this class, give the subclass as the type. E.g. `class Motion extends BaseModel<Motion>`
|
||||||
*/
|
*/
|
||||||
export abstract class BaseModel extends OpenSlidesComponent implements Deserializable, Displayable {
|
export abstract class BaseModel<T = object> extends OpenSlidesComponent
|
||||||
|
implements Deserializable, Displayable, Identifiable {
|
||||||
/**
|
/**
|
||||||
* Register the collection string to the type.
|
* Register the collection string to the type.
|
||||||
* @param collectionString
|
* @param collectionString
|
||||||
@ -57,6 +60,10 @@ export abstract class BaseModel extends OpenSlidesComponent implements Deseriali
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public patchValues(update: Partial<T>): void {
|
||||||
|
Object.assign(this, update);
|
||||||
|
}
|
||||||
|
|
||||||
public abstract getTitle(): string;
|
public abstract getTitle(): string;
|
||||||
|
|
||||||
public getListTitle(): string {
|
public getListTitle(): string {
|
||||||
|
@ -13,5 +13,5 @@ export interface Deserializable {
|
|||||||
* should be used to assign JSON values to the object itself.
|
* should be used to assign JSON values to the object itself.
|
||||||
* @param input
|
* @param input
|
||||||
*/
|
*/
|
||||||
deserialize(input: any): void;
|
deserialize(input: object): void;
|
||||||
}
|
}
|
||||||
|
9
client/src/app/shared/models/base/identifiable.ts
Normal file
9
client/src/app/shared/models/base/identifiable.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* Every object implementing this interface has an id.
|
||||||
|
*/
|
||||||
|
export interface Identifiable {
|
||||||
|
/**
|
||||||
|
* The objects id.
|
||||||
|
*/
|
||||||
|
id: number;
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
import { BaseModel } from './base-model';
|
import { BaseModel } from './base-model';
|
||||||
import { Projectable } from './projectable';
|
import { Projectable } from './projectable';
|
||||||
|
|
||||||
export abstract class ProjectableBaseModel extends BaseModel implements Projectable {
|
export abstract class ProjectableBaseModel extends BaseModel<ProjectableBaseModel> implements Projectable {
|
||||||
protected constructor(collectionString: string, input?: any) {
|
protected constructor(collectionString: string, input?: any) {
|
||||||
super(collectionString, input);
|
super(collectionString, input);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { BaseModel } from '../base/base-model';
|
|||||||
* Representation of chat messages.
|
* Representation of chat messages.
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class ChatMessage extends BaseModel {
|
export class ChatMessage extends BaseModel<ChatMessage> {
|
||||||
public id: number;
|
public id: number;
|
||||||
public message: string;
|
public message: string;
|
||||||
public timestamp: string; // TODO: Type for timestamp
|
public timestamp: string; // TODO: Type for timestamp
|
||||||
|
@ -4,7 +4,7 @@ import { BaseModel } from '../base/base-model';
|
|||||||
* Representation of a projector. Has the nested property "projectiondefaults"
|
* Representation of a projector. Has the nested property "projectiondefaults"
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class Projector extends BaseModel {
|
export class Projector extends BaseModel<Projector> {
|
||||||
public id: number;
|
public id: number;
|
||||||
public elements: Object;
|
public elements: Object;
|
||||||
public scale: number;
|
public scale: number;
|
||||||
|
@ -4,7 +4,7 @@ import { BaseModel } from '../base/base-model';
|
|||||||
* Representation of a tag.
|
* Representation of a tag.
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class Tag extends BaseModel {
|
export class Tag extends BaseModel<Tag> {
|
||||||
public id: number;
|
public id: number;
|
||||||
public name: string;
|
public name: string;
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import { BaseModel } from '../base/base-model';
|
|||||||
* Representation of a motion category. Has the nested property "File"
|
* Representation of a motion category. Has the nested property "File"
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class Category extends BaseModel {
|
export class Category extends BaseModel<Category> {
|
||||||
public id: number;
|
public id: number;
|
||||||
public name: string;
|
public name: string;
|
||||||
public prefix: string;
|
public prefix: string;
|
||||||
|
@ -4,7 +4,7 @@ import { BaseModel } from '../base/base-model';
|
|||||||
* Representation of a motion change recommendation.
|
* Representation of a motion change recommendation.
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class MotionChangeReco extends BaseModel {
|
export class MotionChangeReco extends BaseModel<MotionChangeReco> {
|
||||||
public id: number;
|
public id: number;
|
||||||
public motion_version_id: number;
|
public motion_version_id: number;
|
||||||
public rejected: boolean;
|
public rejected: boolean;
|
||||||
|
@ -4,7 +4,7 @@ import { BaseModel } from '../base/base-model';
|
|||||||
* Representation of a motion category. Has the nested property "File"
|
* Representation of a motion category. Has the nested property "File"
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class MotionCommentSection extends BaseModel {
|
export class MotionCommentSection extends BaseModel<MotionCommentSection> {
|
||||||
public id: number;
|
public id: number;
|
||||||
public name: string;
|
public name: string;
|
||||||
public read_groups_id: number[];
|
public read_groups_id: number[];
|
||||||
|
@ -44,13 +44,6 @@ export class Motion extends AgendaBaseModel {
|
|||||||
super('motions/motion', 'Motion', input);
|
super('motions/motion', 'Motion', input);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* update the values of the motion with new values
|
|
||||||
*/
|
|
||||||
public patchValues(update: object): void {
|
|
||||||
Object.assign(this, update);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the motion submitters userIDs
|
* returns the motion submitters userIDs
|
||||||
*/
|
*/
|
||||||
|
@ -5,7 +5,7 @@ import { WorkflowState } from './workflow-state';
|
|||||||
* Representation of a motion workflow. Has the nested property 'states'
|
* Representation of a motion workflow. Has the nested property 'states'
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class Workflow extends BaseModel {
|
export class Workflow extends BaseModel<Workflow> {
|
||||||
public id: number;
|
public id: number;
|
||||||
public name: string;
|
public name: string;
|
||||||
public states: WorkflowState[];
|
public states: WorkflowState[];
|
||||||
|
@ -4,7 +4,7 @@ import { BaseModel } from '../base/base-model';
|
|||||||
* Representation of user group.
|
* Representation of user group.
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class Group extends BaseModel {
|
export class Group extends BaseModel<Group> {
|
||||||
public id: number;
|
public id: number;
|
||||||
public name: string;
|
public name: string;
|
||||||
public permissions: string[];
|
public permissions: string[];
|
||||||
|
@ -4,7 +4,7 @@ import { BaseModel } from '../base/base-model';
|
|||||||
* Representation of users personal note.
|
* Representation of users personal note.
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
export class PersonalNote extends BaseModel {
|
export class PersonalNote extends BaseModel<PersonalNote> {
|
||||||
public id: number;
|
public id: number;
|
||||||
public user_id: number;
|
public user_id: number;
|
||||||
public notes: Object;
|
public notes: Object;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { BaseViewModel } from '../../base/base-view-model';
|
import { BaseViewModel } from '../../base/base-view-model';
|
||||||
import { Item } from '../../../shared/models/agenda/item';
|
import { Item } from '../../../shared/models/agenda/item';
|
||||||
import { BaseModel } from '../../../shared/models/base/base-model';
|
|
||||||
import { AgendaBaseModel } from '../../../shared/models/base/agenda-base-model';
|
import { AgendaBaseModel } from '../../../shared/models/base/agenda-base-model';
|
||||||
|
|
||||||
export class ViewItem extends BaseViewModel {
|
export class ViewItem extends BaseViewModel {
|
||||||
@ -46,8 +45,8 @@ export class ViewItem extends BaseViewModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateValues(update: BaseModel): void {
|
public updateValues(update: Item): void {
|
||||||
if (update instanceof Item && this.id === update.id) {
|
if (this.id === update.id) {
|
||||||
this._item = update;
|
this._item = update;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ export class AgendaRepositoryService extends BaseRepository<ViewItem, Item> {
|
|||||||
*
|
*
|
||||||
* TODO: used over not-yet-existing detail view
|
* TODO: used over not-yet-existing detail view
|
||||||
*/
|
*/
|
||||||
public save(item: Item, viewUser: ViewItem): Observable<Item> {
|
public update(item: Partial<Item>, viewUser: ViewItem): Observable<Item> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ export class AgendaRepositoryService extends BaseRepository<ViewItem, Item> {
|
|||||||
*
|
*
|
||||||
* TODO: used over not-yet-existing detail view
|
* TODO: used over not-yet-existing detail view
|
||||||
*/
|
*/
|
||||||
public create(item: Item, viewItem: ViewItem): Observable<Item> {
|
public create(item: Item): Observable<Item> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,10 @@ export class ViewAssignment extends BaseViewModel {
|
|||||||
private _agendaItem: Item;
|
private _agendaItem: Item;
|
||||||
private _tags: Tag[];
|
private _tags: Tag[];
|
||||||
|
|
||||||
|
public get id(): number {
|
||||||
|
return this._assignment ? this._assignment.id : null;
|
||||||
|
}
|
||||||
|
|
||||||
public get assignment(): Assignment {
|
public get assignment(): Assignment {
|
||||||
return this._assignment;
|
return this._assignment;
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ export class AssignmentRepositoryService extends BaseRepository<ViewAssignment,
|
|||||||
super(DS, Assignment, [User, Item, Tag]);
|
super(DS, Assignment, [User, Item, Tag]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public save(assignment: Assignment, viewAssignment: ViewAssignment): Observable<Assignment> {
|
public update(assignment: Partial<Assignment>, viewAssignment: ViewAssignment): Observable<Assignment> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ export class AssignmentRepositoryService extends BaseRepository<ViewAssignment,
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public create(assignment: Assignment, viewAssignment: ViewAssignment): Observable<Assignment> {
|
public create(assignment: Assignment): Observable<Assignment> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
|
|||||||
* @param update the update that should be created
|
* @param update the update that should be created
|
||||||
* @param viewModel the view model that the update is based on
|
* @param viewModel the view model that the update is based on
|
||||||
*/
|
*/
|
||||||
public abstract save(update: M, viewModel: V): Observable<M>;
|
public abstract update(update: Partial<M>, viewModel: V): Observable<M>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a given Model
|
* Deletes a given Model
|
||||||
@ -90,7 +90,7 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
|
|||||||
* @param viewModel the view model that the update is based on
|
* @param viewModel the view model that the update is based on
|
||||||
* TODO: remove the viewModel
|
* TODO: remove the viewModel
|
||||||
*/
|
*/
|
||||||
public abstract create(update: M, viewModel: V): Observable<M>;
|
public abstract create(update: M): Observable<M>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a view model out of a base model.
|
* Creates a view model out of a base model.
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
import { BaseModel } from '../../shared/models/base/base-model';
|
import { BaseModel } from '../../shared/models/base/base-model';
|
||||||
import { Displayable } from '../../shared/models/base/displayable';
|
import { Displayable } from '../../shared/models/base/displayable';
|
||||||
|
import { Identifiable } from '../../shared/models/base/identifiable';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for view models. alls view models should have titles.
|
* Base class for view models. alls view models should have titles.
|
||||||
*/
|
*/
|
||||||
export abstract class BaseViewModel implements Displayable {
|
export abstract class BaseViewModel implements Displayable, Identifiable {
|
||||||
|
/**
|
||||||
|
* Force children to have an id.
|
||||||
|
*/
|
||||||
|
public abstract id: number;
|
||||||
|
|
||||||
public abstract updateValues(update: BaseModel): void;
|
public abstract updateValues(update: BaseModel): void;
|
||||||
|
|
||||||
public abstract getTitle(): string;
|
public abstract getTitle(): string;
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
import { BaseViewModel } from '../../base/base-view-model';
|
import { BaseViewModel } from '../../base/base-view-model';
|
||||||
import { Mediafile } from '../../../shared/models/mediafiles/mediafile';
|
import { Mediafile } from '../../../shared/models/mediafiles/mediafile';
|
||||||
import { User } from '../../../shared/models/users/user';
|
import { User } from '../../../shared/models/users/user';
|
||||||
import { BaseModel } from '../../../shared/models/base/base-model';
|
|
||||||
|
|
||||||
export class ViewMediafile extends BaseViewModel {
|
export class ViewMediafile extends BaseViewModel {
|
||||||
private _mediafile: Mediafile;
|
private _mediafile: Mediafile;
|
||||||
private _uploader: User;
|
private _uploader: User;
|
||||||
|
|
||||||
|
public get id(): number {
|
||||||
|
return this._mediafile ? this._mediafile.id : null;
|
||||||
|
}
|
||||||
|
|
||||||
public get mediafile(): Mediafile {
|
public get mediafile(): Mediafile {
|
||||||
return this._mediafile;
|
return this._mediafile;
|
||||||
}
|
}
|
||||||
@ -49,11 +52,9 @@ export class ViewMediafile extends BaseViewModel {
|
|||||||
return this.title;
|
return this.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateValues(update: BaseModel): void {
|
public updateValues(update: Mediafile): void {
|
||||||
if (update instanceof Mediafile) {
|
|
||||||
if (this.mediafile.id === update.id) {
|
if (this.mediafile.id === update.id) {
|
||||||
this._mediafile = update;
|
this._mediafile = update;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ export class MediafileRepositoryService extends BaseRepository<ViewMediafile, Me
|
|||||||
*
|
*
|
||||||
* TODO: used over not-yet-existing detail view
|
* TODO: used over not-yet-existing detail view
|
||||||
*/
|
*/
|
||||||
public save(file: Mediafile, viewFile: ViewMediafile): Observable<Mediafile> {
|
public update(file: Partial<Mediafile>, viewFile: ViewMediafile): Observable<Mediafile> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ export class MediafileRepositoryService extends BaseRepository<ViewMediafile, Me
|
|||||||
*
|
*
|
||||||
* TODO: used over not-yet-existing detail view
|
* TODO: used over not-yet-existing detail view
|
||||||
*/
|
*/
|
||||||
public create(file: Mediafile, viewFile: ViewMediafile): Observable<Mediafile> {
|
public create(file: Mediafile): Observable<Mediafile> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,19 +1,58 @@
|
|||||||
<os-head-bar appName="Category" plusButton=true (plusButtonClicked)=onPlusButton()>
|
<os-head-bar appName="Categories" [plusButton]=true (plusButtonClicked)=onPlusButton()>
|
||||||
</os-head-bar>
|
</os-head-bar>
|
||||||
|
<div class='custom-table-header on-transition-fade'>
|
||||||
<mat-table class='on-transition-fade' [dataSource]="dataSource" matSort>
|
<button mat-button>
|
||||||
<!-- name column -->
|
<fa-icon icon="search"></fa-icon>
|
||||||
<ng-container matColumnDef="name">
|
</button>
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header> Name </mat-header-cell>
|
</div>
|
||||||
<mat-cell *matCellDef="let category"> {{category.name}} </mat-cell>
|
<mat-accordion class="os-card">
|
||||||
</ng-container>
|
<mat-expansion-panel [ngClass]="{new: category.id === undefined}" *ngFor="let category of this.dataSource" (opened)="panelOpening('true', category)" (closed)="panelOpening('false', category)"
|
||||||
|
multiple="false">
|
||||||
<!-- prefix column -->
|
<mat-expansion-panel-header>
|
||||||
<ng-container matColumnDef="prefix">
|
<mat-panel-title *ngIf="!category.edit">
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header> Prefix </mat-header-cell>
|
{{category.name}}
|
||||||
<mat-cell *matCellDef="let category"> {{category.prefix}} </mat-cell>
|
</mat-panel-title>
|
||||||
</ng-container>
|
<mat-panel-title *ngIf="category.edit">
|
||||||
|
{{this.formGroup.get('name').value}}
|
||||||
<mat-header-row *matHeaderRowDef="['name', 'prefix']"></mat-header-row>
|
</mat-panel-title>
|
||||||
<mat-row *matRowDef="let row; columns: ['name', 'prefix']"></mat-row>
|
<mat-panel-description *ngIf="!category.edit">
|
||||||
</mat-table>
|
{{category.prefix}}
|
||||||
|
</mat-panel-description>
|
||||||
|
<mat-panel-description *ngIf="category.edit">
|
||||||
|
{{this.formGroup.get('prefix').value}}
|
||||||
|
</mat-panel-description>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
<form [formGroup]='this.formGroup' *ngIf="category.edit" (keydown)="keyDownFunction($event, category)">
|
||||||
|
<span translate>Edit category details:</span><br>
|
||||||
|
<mat-form-field>
|
||||||
|
<input formControlName="name" matInput placeholder="{{'Name' | translate}}">
|
||||||
|
<small *ngIf="!this.formGroup.controls.name.valid">
|
||||||
|
<span translate>Required</span>
|
||||||
|
</small>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input formControlName="prefix" matInput placeholder="{{'Prefix' | translate}}">
|
||||||
|
<small *ngIf="!this.formGroup.controls.prefix.valid">
|
||||||
|
<span translate>Required</span>
|
||||||
|
</small>
|
||||||
|
</mat-form-field>
|
||||||
|
</form>
|
||||||
|
<mat-action-row>
|
||||||
|
<button *ngIf="!category.edit" mat-button class='on-transition-fade' (click)=onEditButton(category)
|
||||||
|
mat-icon-button>
|
||||||
|
<fa-icon icon='pen'></fa-icon>
|
||||||
|
</button>
|
||||||
|
<button *ngIf="category.edit" mat-button class='on-transition-fade' (click)=onCancelButton(category)
|
||||||
|
mat-icon-button>
|
||||||
|
<fa-icon icon='times'></fa-icon>
|
||||||
|
</button>
|
||||||
|
<button *ngIf="category.edit" mat-button class='on-transition-fade' (click)=onSaveButton(category)
|
||||||
|
mat-icon-button>
|
||||||
|
<fa-icon icon='save'></fa-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-button class='on-transition-fade' (click)=onDeleteButton(category) mat-icon-button>
|
||||||
|
<fa-icon icon='trash'></fa-icon>
|
||||||
|
</button>
|
||||||
|
</mat-action-row>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
</mat-accordion>
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
.button-side {
|
||||||
|
right: 0;
|
||||||
|
top: 0px;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-side {
|
||||||
|
size: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-row {
|
||||||
|
size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new {
|
||||||
|
// put in theme later
|
||||||
|
background-color: lightblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-button {
|
||||||
|
top: 0px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
min-height: 20px;
|
||||||
|
font-size: 10px;
|
||||||
|
box-shadow: none;
|
||||||
|
vertical-align: top;
|
||||||
|
padding: 0 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.onethird {
|
||||||
|
width: 33%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-table-header {
|
||||||
|
// display: none;
|
||||||
|
width: 100%;
|
||||||
|
height: 60px;
|
||||||
|
line-height: 60px;
|
||||||
|
text-align: right;
|
||||||
|
background: white;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
@ -1,59 +1,98 @@
|
|||||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { MatSort, MatTable, MatTableDataSource } from '@angular/material';
|
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { BaseComponent } from '../../../../base.component';
|
import { BaseComponent } from '../../../../base.component';
|
||||||
import { Category } from '../../../../shared/models/motions/category';
|
import { Category } from '../../../../shared/models/motions/category';
|
||||||
import { DataStoreService } from '../../../../core/services/data-store.service';
|
import { CategoryRepositoryService } from '../../services/category-repository.service';
|
||||||
|
import { ViewCategory } from '../../models/view-category';
|
||||||
|
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List view for the categories.
|
* List view for the categories.
|
||||||
*
|
|
||||||
* TODO: Creation of new Categories
|
|
||||||
*/
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'os-category-list',
|
selector: 'os-category-list',
|
||||||
templateUrl: './category-list.component.html',
|
templateUrl: './category-list.component.html',
|
||||||
styleUrls: ['./category-list.component.scss']
|
styleUrls: ['./category-list.component.scss']
|
||||||
})
|
})
|
||||||
export class CategoryListComponent extends BaseComponent implements OnInit {
|
export class CategoryListComponent extends BaseComponent implements OnInit, OnDestroy {
|
||||||
/**
|
/**
|
||||||
* Store the categories
|
* States the edit mode
|
||||||
*/
|
*/
|
||||||
public categoryArray: Array<Category>;
|
public editMode = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will be processed by the mat-table
|
* Source of the Data
|
||||||
*/
|
*/
|
||||||
public dataSource: MatTableDataSource<Category>;
|
public dataSource: Array<ViewCategory>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The table itself.
|
* The current focussed formgroup
|
||||||
*/
|
*/
|
||||||
@ViewChild(MatTable)
|
public formGroup: FormGroup;
|
||||||
public table: MatTable<Category>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sort the Table
|
|
||||||
*/
|
|
||||||
@ViewChild(MatSort)
|
|
||||||
public sort: MatSort;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The usual component constructor
|
* The usual component constructor
|
||||||
* @param titleService
|
* @param titleService
|
||||||
* @param translate
|
* @param translate
|
||||||
|
* @param repo
|
||||||
|
* @param formBuilder
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
protected titleService: Title,
|
protected titleService: Title,
|
||||||
protected translate: TranslateService,
|
protected translate: TranslateService,
|
||||||
protected DS: DataStoreService
|
private repo: CategoryRepositoryService,
|
||||||
|
private formBuilder: FormBuilder
|
||||||
) {
|
) {
|
||||||
super(titleService, translate);
|
super(titleService, translate);
|
||||||
|
this.formGroup = this.formBuilder.group({
|
||||||
|
name: ['', Validators.required],
|
||||||
|
prefix: ['', Validators.required]
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On Destroy Function
|
||||||
|
*
|
||||||
|
* Saves the edits
|
||||||
|
*/
|
||||||
|
public ngOnDestroy(): void {
|
||||||
|
this.dataSource.forEach(viewCategory => {
|
||||||
|
if (viewCategory.edit && viewCategory.opened) {
|
||||||
|
const nameControl = this.formGroup.get('name');
|
||||||
|
const prefixControl = this.formGroup.get('prefix');
|
||||||
|
const nameValue = nameControl.value;
|
||||||
|
const prefixValue = prefixControl.value;
|
||||||
|
viewCategory.name = nameValue;
|
||||||
|
viewCategory.prefix = prefixValue;
|
||||||
|
this.saveCategory(viewCategory);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event on Key Down in form
|
||||||
|
*/
|
||||||
|
public keyDownFunction(event: KeyboardEvent, viewCategory: ViewCategory): void {
|
||||||
|
if (event.keyCode === 13) {
|
||||||
|
this.onSaveButton(viewCategory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the Datamodel in the repo
|
||||||
|
* @param viewCategory
|
||||||
|
*/
|
||||||
|
private saveCategory(viewCategory: ViewCategory): void {
|
||||||
|
if (this.repo.osInDataStore(viewCategory)) {
|
||||||
|
this.repo.update(viewCategory.category).subscribe();
|
||||||
|
} else {
|
||||||
|
this.repo.create(viewCategory.category, viewCategory).subscribe();
|
||||||
|
}
|
||||||
|
viewCategory.edit = false;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Init function.
|
* Init function.
|
||||||
*
|
*
|
||||||
@ -61,26 +100,127 @@ export class CategoryListComponent extends BaseComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
super.setTitle('Category');
|
super.setTitle('Category');
|
||||||
this.categoryArray = this.DS.getAll(Category);
|
this.repo.getViewModelListObservable().subscribe(newViewCategories => {
|
||||||
this.dataSource = new MatTableDataSource(this.categoryArray);
|
this.dataSource = newViewCategories;
|
||||||
this.dataSource.sort = this.sort;
|
});
|
||||||
|
this.sortDataSource();
|
||||||
|
}
|
||||||
|
|
||||||
// Observe DataStore for motions. Initially, executes once for every motion.
|
/**
|
||||||
// The alternative approach is to put the observable as DataSource to the table
|
* Add a new Category.
|
||||||
this.DS.changeObservable.subscribe(newModel => {
|
*/
|
||||||
if (newModel instanceof Category) {
|
public onPlusButton(): void {
|
||||||
this.categoryArray = this.DS.getAll(Category);
|
let noNewOnes = true;
|
||||||
this.dataSource.data = this.categoryArray;
|
this.dataSource.forEach(viewCategory => {
|
||||||
|
if (viewCategory.id === undefined) {
|
||||||
|
noNewOnes = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (noNewOnes) {
|
||||||
|
const newCategory = new Category();
|
||||||
|
newCategory.id = undefined;
|
||||||
|
newCategory.name = this.translate.instant('Name');
|
||||||
|
newCategory.prefix = this.translate.instant('Prefix');
|
||||||
|
const newViewCategory = new ViewCategory(newCategory);
|
||||||
|
newViewCategory.opened = true;
|
||||||
|
this.dataSource.reverse();
|
||||||
|
this.dataSource.push(newViewCategory);
|
||||||
|
this.dataSource.reverse();
|
||||||
|
this.editMode = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executed on edit button
|
||||||
|
* @param viewCategory
|
||||||
|
*/
|
||||||
|
public onEditButton(viewCategory: ViewCategory): void {
|
||||||
|
viewCategory.edit = true;
|
||||||
|
viewCategory.synced = false;
|
||||||
|
this.editMode = true;
|
||||||
|
const nameControl = this.formGroup.get('name');
|
||||||
|
const prefixControl = this.formGroup.get('prefix');
|
||||||
|
nameControl.setValue(viewCategory.name);
|
||||||
|
prefixControl.setValue(viewCategory.prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the categories
|
||||||
|
*/
|
||||||
|
public onSaveButton(viewCategory: ViewCategory): void {
|
||||||
|
if (this.formGroup.controls.name.valid && this.formGroup.controls.prefix.valid) {
|
||||||
|
this.editMode = false;
|
||||||
|
const nameControl = this.formGroup.get('name');
|
||||||
|
const prefixControl = this.formGroup.get('prefix');
|
||||||
|
const nameValue = nameControl.value;
|
||||||
|
const prefixValue = prefixControl.value;
|
||||||
|
if (
|
||||||
|
viewCategory.id === undefined ||
|
||||||
|
nameValue !== viewCategory.name ||
|
||||||
|
prefixValue !== viewCategory.prefix
|
||||||
|
) {
|
||||||
|
viewCategory.prefix = prefixValue;
|
||||||
|
viewCategory.name = nameValue;
|
||||||
|
this.saveCategory(viewCategory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.sortDataSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sorts the datasource by prefix alphabetically
|
||||||
|
*/
|
||||||
|
protected sortDataSource(): void {
|
||||||
|
this.dataSource.sort((viewCategory1, viewCategory2) => {
|
||||||
|
if (viewCategory1.prefix > viewCategory2.prefix) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (viewCategory1.prefix < viewCategory2.prefix) {
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new Category.
|
* executed on cancel button
|
||||||
*
|
* @param viewCategory
|
||||||
* TODO: Not yet implemented
|
|
||||||
*/
|
*/
|
||||||
public onPlusButton(): void {
|
public onCancelButton(viewCategory: ViewCategory): void {
|
||||||
console.log('Add New Category');
|
viewCategory.edit = false;
|
||||||
|
this.editMode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* is executed, when the delete button is pressed
|
||||||
|
*/
|
||||||
|
public onDeleteButton(viewCategory: ViewCategory): void {
|
||||||
|
if (this.repo.osInDataStore(viewCategory) && viewCategory.id !== undefined) {
|
||||||
|
this.repo.delete(viewCategory).subscribe();
|
||||||
|
}
|
||||||
|
const index = this.dataSource.indexOf(viewCategory, 0);
|
||||||
|
if (index > -1) {
|
||||||
|
this.dataSource.splice(index, 1);
|
||||||
|
}
|
||||||
|
// if no category is there, we setill have to be able to create one
|
||||||
|
if (this.dataSource.length < 1) {
|
||||||
|
this.editMode = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is executed when a mat-extension-panel is opened or closed
|
||||||
|
* @param open true if opened, false if being closed
|
||||||
|
* @param category the category in the panel
|
||||||
|
*/
|
||||||
|
public panelOpening(open: boolean, category: ViewCategory): void {
|
||||||
|
category.opened = open as boolean;
|
||||||
|
if (category.edit === true) {
|
||||||
|
this.onSaveButton(category);
|
||||||
|
this.onCancelButton(category);
|
||||||
|
}
|
||||||
|
if (!open) {
|
||||||
|
category.edit = false;
|
||||||
|
this.editMode = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<span *ngIf="motion && !editMotion"> {{motion.title}}</span>
|
<span *ngIf="motion && !editMotion"> {{motion.title}}</span>
|
||||||
<span *ngIf="editMotion"> {{contentForm.get('title').value}}</span>
|
<span *ngIf="editMotion"> {{contentForm.get('title').value}}</span>
|
||||||
<br>
|
<br>
|
||||||
<div *ngIf="motion" class='motion-submitter'>
|
<div *ngIf="motion && !newMotion" class='motion-submitter'>
|
||||||
<span translate>by</span> {{motion.submitters}}
|
<span translate>by</span> {{motion.submitters}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -148,7 +148,8 @@
|
|||||||
<div *ngIf="motion && motion.submitters || editMotion">
|
<div *ngIf="motion && motion.submitters || editMotion">
|
||||||
<div *ngIf="editMotion && newMotion">
|
<div *ngIf="editMotion && newMotion">
|
||||||
<div *ngIf="motion && editMotion">
|
<div *ngIf="motion && editMotion">
|
||||||
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="this.metaInfoForm.get('submitters')" multiple="true" listname="Submitter" [InputListValues]="getAllUsers()"></os-search-value-selector>
|
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="this.metaInfoForm.get('submitters_id')"
|
||||||
|
[multiple]="true" listname="Submitter" [InputListValues]="this.submitterObserver"></os-search-value-selector>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!editMotion || !newMotion">
|
<div *ngIf="!editMotion || !newMotion">
|
||||||
@ -164,7 +165,8 @@
|
|||||||
<!-- print all motion supporters -->
|
<!-- print all motion supporters -->
|
||||||
<div *ngIf="editMotion">
|
<div *ngIf="editMotion">
|
||||||
<div *ngIf="motion && editMotion">
|
<div *ngIf="motion && editMotion">
|
||||||
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="this.metaInfoForm.get('supporters_id')" multiple="true" listname="Supporter" [InputListValues]="getAllUsers()"></os-search-value-selector>
|
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="this.metaInfoForm.get('supporters_id')"
|
||||||
|
[multiple]="true" listname="Supporter" [InputListValues]="this.supporterObserver"></os-search-value-selector>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!editMotion && motion.hasSupporters()">
|
<div *ngIf="!editMotion && motion.hasSupporters()">
|
||||||
@ -218,11 +220,12 @@
|
|||||||
<!-- Category -->
|
<!-- Category -->
|
||||||
<div *ngIf="motion && motion.categoryId || editMotion">
|
<div *ngIf="motion && motion.categoryId || editMotion">
|
||||||
<div *ngIf='!editMotion'>
|
<div *ngIf='!editMotion'>
|
||||||
<h3 translate> Category</h3>
|
<h3 translate>Category</h3>
|
||||||
{{motion.category}}
|
{{motion.category}}
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="editMotion">
|
<div *ngIf="editMotion">
|
||||||
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="this.metaInfoForm.get('category_id')" multiple="false" listname="Category" [InputListValues]="getMotionCategories()"></os-search-value-selector>
|
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="this.metaInfoForm.get('category_id')"
|
||||||
|
[multiple]="false" listname="Category" [InputListValues]="this.categoryObserver"></os-search-value-selector>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -10,6 +10,9 @@ import { MotionRepositoryService } from '../../services/motion-repository.servic
|
|||||||
import { ViewMotion } from '../../models/view-motion';
|
import { ViewMotion } from '../../models/view-motion';
|
||||||
import { User } from '../../../../shared/models/users/user';
|
import { User } from '../../../../shared/models/users/user';
|
||||||
import { DataStoreService } from '../../../../core/services/data-store.service';
|
import { DataStoreService } from '../../../../core/services/data-store.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { Motion } from '../../../../shared/models/motions/motion';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for the motion detail view
|
* Component for the motion detail view
|
||||||
@ -64,6 +67,21 @@ export class MotionDetailComponent extends BaseComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
public motionCopy: ViewMotion;
|
public motionCopy: ViewMotion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subject for the Categories
|
||||||
|
*/
|
||||||
|
public categoryObserver: BehaviorSubject<Array<Category>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subject for the Submitters
|
||||||
|
*/
|
||||||
|
public submitterObserver: BehaviorSubject<Array<User>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subject for the Supporters
|
||||||
|
*/
|
||||||
|
public supporterObserver: BehaviorSubject<Array<User>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constuct the detail view.
|
* Constuct the detail view.
|
||||||
*
|
*
|
||||||
@ -72,6 +90,7 @@ export class MotionDetailComponent extends BaseComponent implements OnInit {
|
|||||||
* @param route determine if this is a new or an existing motion
|
* @param route determine if this is a new or an existing motion
|
||||||
* @param formBuilder For reactive forms. Form Group and Form Control
|
* @param formBuilder For reactive forms. Form Group and Form Control
|
||||||
* @param repo: Motion Repository
|
* @param repo: Motion Repository
|
||||||
|
* @param translate: Translation Service
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
public vp: ViewportService,
|
public vp: ViewportService,
|
||||||
@ -79,7 +98,8 @@ export class MotionDetailComponent extends BaseComponent implements OnInit {
|
|||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private repo: MotionRepositoryService,
|
private repo: MotionRepositoryService,
|
||||||
private DS: DataStoreService
|
private DS: DataStoreService,
|
||||||
|
protected translate: TranslateService
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.createForm();
|
this.createForm();
|
||||||
@ -100,6 +120,21 @@ export class MotionDetailComponent extends BaseComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// Initial Filling of the Subjects
|
||||||
|
this.submitterObserver = new BehaviorSubject(DS.getAll(User));
|
||||||
|
this.supporterObserver = new BehaviorSubject(DS.getAll(User));
|
||||||
|
this.categoryObserver = new BehaviorSubject(DS.getAll(Category));
|
||||||
|
|
||||||
|
// Make sure the subjects are updated, when a new Model for the type arrives
|
||||||
|
this.DS.changeObservable.subscribe(newModel => {
|
||||||
|
if (newModel instanceof User) {
|
||||||
|
this.submitterObserver.next(DS.getAll(User));
|
||||||
|
this.supporterObserver.next(DS.getAll(User));
|
||||||
|
}
|
||||||
|
if (newModel instanceof Category) {
|
||||||
|
this.categoryObserver.next(DS.getAll(Category));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -108,8 +143,8 @@ export class MotionDetailComponent extends BaseComponent implements OnInit {
|
|||||||
public patchForm(formMotion: ViewMotion): void {
|
public patchForm(formMotion: ViewMotion): void {
|
||||||
this.metaInfoForm.patchValue({
|
this.metaInfoForm.patchValue({
|
||||||
category_id: formMotion.categoryId,
|
category_id: formMotion.categoryId,
|
||||||
supporters_id: formMotion.supporters,
|
supporters_id: formMotion.supporterIds,
|
||||||
submitters: formMotion.submitters,
|
submitters_id: formMotion.submitterIds,
|
||||||
state_id: formMotion.stateId,
|
state_id: formMotion.stateId,
|
||||||
recommendation_id: formMotion.recommendationId,
|
recommendation_id: formMotion.recommendationId,
|
||||||
identifier: formMotion.identifier,
|
identifier: formMotion.identifier,
|
||||||
@ -133,7 +168,7 @@ export class MotionDetailComponent extends BaseComponent implements OnInit {
|
|||||||
category_id: [''],
|
category_id: [''],
|
||||||
state_id: [''],
|
state_id: [''],
|
||||||
recommendation_id: [''],
|
recommendation_id: [''],
|
||||||
submitters: [''],
|
submitters_id: [''],
|
||||||
supporters_id: [''],
|
supporters_id: [''],
|
||||||
origin: ['']
|
origin: ['']
|
||||||
});
|
});
|
||||||
@ -157,22 +192,18 @@ export class MotionDetailComponent extends BaseComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
public saveMotion(): void {
|
public saveMotion(): void {
|
||||||
const newMotionValues = { ...this.metaInfoForm.value, ...this.contentForm.value };
|
const newMotionValues = { ...this.metaInfoForm.value, ...this.contentForm.value };
|
||||||
|
const fromForm = new Motion();
|
||||||
|
fromForm.deserialize(newMotionValues);
|
||||||
|
|
||||||
if (this.newMotion) {
|
if (this.newMotion) {
|
||||||
this.repo.create(newMotionValues).subscribe(response => {
|
this.repo.create(fromForm).subscribe(response => {
|
||||||
this.router.navigate(['./motions/' + response.id]);
|
this.router.navigate(['./motions/' + response.id]);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.repo.save(newMotionValues, this.motionCopy).subscribe();
|
this.repo.update(fromForm, this.motionCopy).subscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* return all Categories
|
|
||||||
*/
|
|
||||||
public getMotionCategories(): Category[] {
|
|
||||||
return this.DS.getAll(Category);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Click on the edit button (pen-symbol)
|
* Click on the edit button (pen-symbol)
|
||||||
*/
|
*/
|
||||||
@ -215,13 +246,6 @@ export class MotionDetailComponent extends BaseComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* returns all Possible supporters
|
|
||||||
*/
|
|
||||||
public getAllUsers(): User[] {
|
|
||||||
return this.DS.getAll(User);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init. Does nothing here.
|
* Init. Does nothing here.
|
||||||
*/
|
*/
|
||||||
|
98
client/src/app/site/motions/models/view-category.ts
Normal file
98
client/src/app/site/motions/models/view-category.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import { Category } from '../../../shared/models/motions/category';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { BaseViewModel } from '../../base/base-view-model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Category class for the View
|
||||||
|
*
|
||||||
|
* Stores a Category including all (implicit) references
|
||||||
|
* Provides "safe" access to variables and functions in {@link Category}
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
export class ViewCategory extends BaseViewModel {
|
||||||
|
private _category: Category;
|
||||||
|
private _edit: boolean;
|
||||||
|
private _synced: boolean;
|
||||||
|
private _opened: boolean;
|
||||||
|
|
||||||
|
public get category(): Category {
|
||||||
|
return this._category;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get id(): number {
|
||||||
|
return this.category ? this.category.id : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get name(): string {
|
||||||
|
return this.category ? this.category.name : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get prefix(): string {
|
||||||
|
return this.category ? this.category.prefix : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set synced(bol: boolean) {
|
||||||
|
this._synced = bol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set edit(bol: boolean) {
|
||||||
|
this._edit = bol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set opened(bol: boolean) {
|
||||||
|
this._opened = bol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set prefix(pref: string) {
|
||||||
|
this._category.prefix = pref;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set name(nam: string) {
|
||||||
|
this._category.name = nam;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get opened(): boolean {
|
||||||
|
return this._opened;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get synced(): boolean {
|
||||||
|
return this._synced;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get edit(): boolean {
|
||||||
|
return this._edit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor(category?: Category, id?: number, prefix?: string, name?: string) {
|
||||||
|
super();
|
||||||
|
if (!category) {
|
||||||
|
category = new Category();
|
||||||
|
category.id = id;
|
||||||
|
category.name = name;
|
||||||
|
category.prefix = prefix;
|
||||||
|
}
|
||||||
|
this._category = category;
|
||||||
|
this._edit = false;
|
||||||
|
this._synced = true;
|
||||||
|
this._opened = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTitle(translate?: TranslateService): string {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the local objects if required
|
||||||
|
* @param update
|
||||||
|
*/
|
||||||
|
public updateValues(update: Category): void {
|
||||||
|
this._category = update;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duplicate this motion into a copy of itself
|
||||||
|
*/
|
||||||
|
public copy(): ViewCategory {
|
||||||
|
return new ViewCategory(this._category);
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,6 @@ import { WorkflowState } from '../../../shared/models/motions/workflow-state';
|
|||||||
import { BaseModel } from '../../../shared/models/base/base-model';
|
import { BaseModel } from '../../../shared/models/base/base-model';
|
||||||
import { BaseViewModel } from '../../base/base-view-model';
|
import { BaseViewModel } from '../../base/base-view-model';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Motion class for the View
|
* Motion class for the View
|
||||||
*
|
*
|
||||||
@ -58,10 +57,18 @@ export class ViewMotion extends BaseViewModel {
|
|||||||
return this._submitters;
|
return this._submitters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get submitterIds(): number[] {
|
||||||
|
return this.motion ? this.motion.submitters_id : null;
|
||||||
|
}
|
||||||
|
|
||||||
public get supporters(): User[] {
|
public get supporters(): User[] {
|
||||||
return this._supporters;
|
return this._supporters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get supporterIds(): number[] {
|
||||||
|
return this.motion ? this.motion.supporters_id : null;
|
||||||
|
}
|
||||||
|
|
||||||
public get workflow(): Workflow {
|
public get workflow(): Workflow {
|
||||||
return this._workflow;
|
return this._workflow;
|
||||||
}
|
}
|
||||||
@ -101,6 +108,32 @@ export class ViewMotion extends BaseViewModel {
|
|||||||
return this.state && this.workflow ? this.state.getNextStates(this.workflow) : null;
|
return this.state && this.workflow ? this.state.getNextStates(this.workflow) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public set supporters(users: User[]) {
|
||||||
|
const userIDArr: number[] = [];
|
||||||
|
users.forEach(user => {
|
||||||
|
userIDArr.push(user.id);
|
||||||
|
});
|
||||||
|
this._supporters = users;
|
||||||
|
this._motion.supporters_id = userIDArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set submitters(users: User[]) {
|
||||||
|
// For the newer backend with weight:
|
||||||
|
// const submitterArr: MotionSubmitter[] = []
|
||||||
|
// users.forEach(user => {
|
||||||
|
// const motionSub = new MotionSubmitter();
|
||||||
|
// submitterArr.push(motionSub);
|
||||||
|
// });
|
||||||
|
// this._motion.submitters = submitterArr;
|
||||||
|
this._submitters = users;
|
||||||
|
const submitterIDArr: number[] = [];
|
||||||
|
// for the older backend:
|
||||||
|
users.forEach(user => {
|
||||||
|
submitterIDArr.push(user.id);
|
||||||
|
});
|
||||||
|
this._motion.submitters_id = submitterIDArr;
|
||||||
|
}
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
motion?: Motion,
|
motion?: Motion,
|
||||||
category?: Category,
|
category?: Category,
|
||||||
@ -129,16 +162,30 @@ export class ViewMotion extends BaseViewModel {
|
|||||||
*/
|
*/
|
||||||
public updateValues(update: BaseModel): void {
|
public updateValues(update: BaseModel): void {
|
||||||
if (update instanceof Workflow) {
|
if (update instanceof Workflow) {
|
||||||
if (this.motion && update.id === this.motion.workflow_id) {
|
this.updateWorkflow(update as Workflow);
|
||||||
this._workflow = update as Workflow;
|
|
||||||
}
|
|
||||||
} else if (update instanceof Category) {
|
} else if (update instanceof Category) {
|
||||||
|
this.updateCategory(update as Category);
|
||||||
|
}
|
||||||
|
// TODO: There is no way (yet) to add Submitters to a motion
|
||||||
|
// Thus, this feature could not be tested
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the Category
|
||||||
|
*/
|
||||||
|
public updateCategory(update: Category): void {
|
||||||
if (this.motion && update.id === this.motion.category_id) {
|
if (this.motion && update.id === this.motion.category_id) {
|
||||||
this._category = update as Category;
|
this._category = update as Category;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: There is no way (yet) to add Submitters to a motion
|
|
||||||
// Thus, this feature could not be tested
|
/**
|
||||||
|
* updates the Workflow
|
||||||
|
*/
|
||||||
|
public updateWorkflow(update: Workflow): void {
|
||||||
|
if (this.motion && update.id === this.motion.workflow_id) {
|
||||||
|
this._workflow = update as Workflow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public hasSupporters(): boolean {
|
public hasSupporters(): boolean {
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
import { TestBed, inject } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CategoryRepositoryService } from './category-repository.service';
|
||||||
|
|
||||||
|
describe('CategoryRepositoryService', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [CategoryRepositoryService]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', inject([CategoryRepositoryService], (service: CategoryRepositoryService) => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
}));
|
||||||
|
});
|
@ -0,0 +1,74 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Category } from '../../../shared/models/motions/category';
|
||||||
|
import { ViewCategory } from '../models/view-category';
|
||||||
|
import { DataSendService } from '../../../core/services/data-send.service';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { DataStoreService } from '../../../core/services/data-store.service';
|
||||||
|
import { BaseRepository } from '../../base/base-repository';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repository Services for Categories
|
||||||
|
*
|
||||||
|
* The repository is meant to process domain objects (those found under
|
||||||
|
* shared/models), so components can display them and interact with them.
|
||||||
|
*
|
||||||
|
* Rather than manipulating models directly, the repository is meant to
|
||||||
|
* inform the {@link DataSendService} about changes which will send
|
||||||
|
* them to the Server.
|
||||||
|
*/
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class CategoryRepositoryService extends BaseRepository<ViewCategory, Category> {
|
||||||
|
/**
|
||||||
|
* Creates a CategoryRepository
|
||||||
|
* Converts existing and incoming category to ViewCategories
|
||||||
|
* Handles CRUD using an observer to the DataStore
|
||||||
|
* @param DataSend
|
||||||
|
*/
|
||||||
|
public constructor(protected DS: DataStoreService, private dataSend: DataSendService) {
|
||||||
|
super(DS, Category);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected createViewModel(category: Category): ViewCategory {
|
||||||
|
return new ViewCategory(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
public create(update: Category, viewCategory?: ViewCategory): Observable<any> {
|
||||||
|
console.log('update: ', update);
|
||||||
|
console.log('viewCategory: ', viewCategory);
|
||||||
|
if (this.osInDataStore(viewCategory)) {
|
||||||
|
return this.update(update, viewCategory);
|
||||||
|
} else {
|
||||||
|
return this.dataSend.saveModel(viewCategory.category);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(update: Category, viewCategory?: ViewCategory): Observable<any> {
|
||||||
|
let updateCategory: Category;
|
||||||
|
if (viewCategory) {
|
||||||
|
updateCategory = viewCategory.category;
|
||||||
|
} else {
|
||||||
|
updateCategory = new Category();
|
||||||
|
}
|
||||||
|
updateCategory.patchValues(update);
|
||||||
|
return this.dataSend.saveModel(updateCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public delete(viewCategory: ViewCategory): Observable<any> {
|
||||||
|
const category = viewCategory.category;
|
||||||
|
return this.dataSend.delete(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a Catagory is on the server already
|
||||||
|
* @param viewCategory the category to check if it is already on the server
|
||||||
|
*/
|
||||||
|
public osInDataStore(viewCategory: ViewCategory): boolean {
|
||||||
|
const serverCategoryArray = this.DS.getAll(Category);
|
||||||
|
if (serverCategoryArray.find(cat => cat.id === viewCategory.id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -65,8 +65,8 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
|||||||
* @param viewMotion The View Motion. If not present, a new motion will be created
|
* @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()
|
* TODO: Remove the viewMotion and make it actually distignuishable from save()
|
||||||
*/
|
*/
|
||||||
public create(update: any, viewMotion?: ViewMotion): Observable<any> {
|
public create(motion: Motion): Observable<any> {
|
||||||
return this.save(update, viewMotion);
|
return this.dataSend.saveModel(motion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,39 +78,10 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
|||||||
* @param update the form data containing the update values
|
* @param update the form data containing the update values
|
||||||
* @param viewMotion The View Motion. If not present, a new motion will be created
|
* @param viewMotion The View Motion. If not present, a new motion will be created
|
||||||
*/
|
*/
|
||||||
public save(update: any, viewMotion?: ViewMotion): Observable<any> {
|
public update(update: Partial<Motion>, viewMotion: ViewMotion): Observable<any> {
|
||||||
let updateMotion: Motion;
|
const motion = viewMotion.motion;
|
||||||
if (viewMotion) {
|
motion.patchValues(update);
|
||||||
// implies that an existing motion was updated
|
return this.dataSend.saveModel(motion);
|
||||||
updateMotion = viewMotion.motion;
|
|
||||||
} else {
|
|
||||||
// implies that a new motion was created
|
|
||||||
updateMotion = new Motion();
|
|
||||||
}
|
|
||||||
// submitters: User[] -> submitter: MotionSubmitter[]
|
|
||||||
const submitters = update.submitters as User[];
|
|
||||||
// The server doesn't really accept MotionSubmitter arrays on create.
|
|
||||||
// We simply need to send an number[] on create.
|
|
||||||
// MotionSubmitter[] should be send on update
|
|
||||||
update.submitters = undefined;
|
|
||||||
const submitterIds: number[] = [];
|
|
||||||
if (submitters.length > 0) {
|
|
||||||
submitters.forEach(submitter => {
|
|
||||||
submitterIds.push(submitter.id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
update.submitters_id = submitterIds;
|
|
||||||
// supporters[]: User -> supporters_id: number[];
|
|
||||||
const supporters = update.supporters_id as User[];
|
|
||||||
const supporterIds: number[] = [];
|
|
||||||
if (supporters.length > 0) {
|
|
||||||
supporters.forEach(supporter => {
|
|
||||||
supporterIds.push(supporter.id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
update.supporters_id = supporterIds;
|
|
||||||
updateMotion.patchValues(update);
|
|
||||||
return this.dataSend.saveModel(updateMotion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { BaseViewModel } from '../../base/base-view-model';
|
import { BaseViewModel } from '../../base/base-view-model';
|
||||||
import { BaseModel } from '../../../shared/models/base/base-model';
|
|
||||||
import { Config } from '../../../shared/models/core/config';
|
import { Config } from '../../../shared/models/core/config';
|
||||||
|
|
||||||
export class ViewConfig extends BaseViewModel {
|
export class ViewConfig extends BaseViewModel {
|
||||||
@ -30,9 +29,7 @@ export class ViewConfig extends BaseViewModel {
|
|||||||
return this.key;
|
return this.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateValues(update: BaseModel): void {
|
public updateValues(update: Config): void {
|
||||||
if (update instanceof Config && this.id === update.id) {
|
|
||||||
this._config = update;
|
this._config = update;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ export class ConfigRepositoryService extends BaseRepository<ViewConfig, Config>
|
|||||||
*
|
*
|
||||||
* TODO: used over not-yet-existing detail view
|
* TODO: used over not-yet-existing detail view
|
||||||
*/
|
*/
|
||||||
public save(config: Config, viewConfig: ViewConfig): Observable<Config> {
|
public update(config: Partial<Config>, viewConfig: ViewConfig): Observable<Config> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ export class ConfigRepositoryService extends BaseRepository<ViewConfig, Config>
|
|||||||
*
|
*
|
||||||
* Function exists solely to correctly implement {@link BaseRepository}
|
* Function exists solely to correctly implement {@link BaseRepository}
|
||||||
*/
|
*/
|
||||||
public create(config: Config, viewConfig: ViewConfig): Observable<Config> {
|
public create(config: Config): Observable<Config> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,10 @@ export class ViewUser extends BaseViewModel {
|
|||||||
private _user: User;
|
private _user: User;
|
||||||
private _groups: Group[];
|
private _groups: Group[];
|
||||||
|
|
||||||
|
public get id(): number {
|
||||||
|
return this._user ? this._user.id : null;
|
||||||
|
}
|
||||||
|
|
||||||
public get user(): User {
|
public get user(): User {
|
||||||
return this._user;
|
return this._user;
|
||||||
}
|
}
|
||||||
@ -47,22 +51,30 @@ export class ViewUser extends BaseViewModel {
|
|||||||
console.log('replace group - not yet implemented, ', newGroup);
|
console.log('replace group - not yet implemented, ', newGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates values. Triggered through observables.
|
|
||||||
*
|
|
||||||
* @param update a new User or Group
|
|
||||||
*/
|
|
||||||
public updateValues(update: BaseModel): void {
|
public updateValues(update: BaseModel): void {
|
||||||
if (update instanceof User) {
|
if (update instanceof Group) {
|
||||||
if (this.user.id === update.id) {
|
this.updateGroup(update as Group);
|
||||||
this._user = update;
|
|
||||||
}
|
}
|
||||||
} else if (update instanceof Group) {
|
if (update instanceof User) {
|
||||||
|
this.updateUser(update as User);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateGroup(update: Group): void {
|
||||||
if (this.user && this.user.groups_id) {
|
if (this.user && this.user.groups_id) {
|
||||||
if (this.user.containsGroupId(update.id)) {
|
if (this.user.containsGroupId(update.id)) {
|
||||||
this.replaceGroup(update);
|
this.replaceGroup(update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Updates values. Triggered through observables.
|
||||||
|
*
|
||||||
|
* @param update a new User or Group
|
||||||
|
*/
|
||||||
|
public updateUser(update: User): void {
|
||||||
|
if (this.user.id === update.id) {
|
||||||
|
this._user = update;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
|
|||||||
*
|
*
|
||||||
* TODO: used over not-yet-existing detail view
|
* TODO: used over not-yet-existing detail view
|
||||||
*/
|
*/
|
||||||
public save(user: User, viewUser: ViewUser): Observable<User> {
|
public update(user: Partial<User>, viewUser: ViewUser): Observable<User> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
|
|||||||
*
|
*
|
||||||
* TODO: used over not-yet-existing detail view
|
* TODO: used over not-yet-existing detail view
|
||||||
*/
|
*/
|
||||||
public create(user: User, viewFile: ViewUser): Observable<User> {
|
public create(user: User): Observable<User> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,12 +21,6 @@ body {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**the plus button in Motion, Agenda, etc*/
|
|
||||||
.generic-plus-button {
|
|
||||||
bottom: -30px;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.generic-mini-button {
|
.generic-mini-button {
|
||||||
bottom: -28px;
|
bottom: -28px;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
Loading…
Reference in New Issue
Block a user