From f1da2689b95de6e5f8d10c79ec84727236e8b0de Mon Sep 17 00:00:00 2001 From: Sean Engelhardt Date: Thu, 16 Aug 2018 17:03:39 +0200 Subject: [PATCH 1/6] Add motion update form --- client/package-lock.json | 28 ++- .../shared/directives/os-perms.directive.ts | 1 - .../src/app/shared/models/motions/motion.ts | 82 ++++++--- .../shared/models/motions/workflow-state.ts | 4 + .../src/app/shared/models/motions/workflow.ts | 8 +- client/src/app/shared/models/users/user.ts | 35 ++++ client/src/app/shared/shared.module.ts | 6 + .../motion-detail.component.html | 162 ++++++++++++------ .../motion-detail.component.scss | 20 ++- .../motion-detail/motion-detail.component.ts | 60 ++++++- .../motion-list/motion-list.component.html | 6 +- .../motion-list/motion-list.component.ts | 9 +- client/src/app/site/site.component.ts | 6 +- 13 files changed, 316 insertions(+), 111 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index eb8bf9398..3e88d00f8 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -4645,12 +4645,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4665,17 +4667,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -4792,7 +4797,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -4804,6 +4810,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4818,6 +4825,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4825,12 +4833,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -4849,6 +4859,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -4929,7 +4940,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -4941,6 +4953,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -5062,6 +5075,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", diff --git a/client/src/app/shared/directives/os-perms.directive.ts b/client/src/app/shared/directives/os-perms.directive.ts index 95f107f94..02e896325 100644 --- a/client/src/app/shared/directives/os-perms.directive.ts +++ b/client/src/app/shared/directives/os-perms.directive.ts @@ -78,7 +78,6 @@ export class OsPermsDirective extends OpenSlidesComponent { private updateView(): void { if (this.checkPermissions()) { // will just render the page normally - console.log('do show: ', this.template, ' - ', this.viewContainer); this.viewContainer.createEmbeddedView(this.template); } else { // will remove the content of the container diff --git a/client/src/app/shared/models/motions/motion.ts b/client/src/app/shared/models/motions/motion.ts index 955b76776..ef639c476 100644 --- a/client/src/app/shared/models/motions/motion.ts +++ b/client/src/app/shared/models/motions/motion.ts @@ -6,6 +6,7 @@ import { Config } from '../core/config'; import { Workflow } from './workflow'; import { User } from '../users/user'; import { Category } from './category'; +import { WorkflowState } from './workflow-state'; /** * Representation of Motion. @@ -123,7 +124,7 @@ export class Motion extends BaseModel { get submitterName() { const mainSubmitter = this.DS.get(User, this.submitters[0].user_id) as User; if (mainSubmitter) { - return mainSubmitter.username; + return mainSubmitter; } else { return ''; } @@ -132,40 +133,74 @@ export class Motion extends BaseModel { /** * get the category of a motion as object */ - get category() { + get category(): any { if (this.category_id) { const motionCategory = this.DS.get(Category, this.category_id); - return motionCategory; + return motionCategory as Category; } else { return 'none'; } } + /** + * Set the category in the motion + */ + set category(newCategory: any) { + this.category_id = newCategory.id; + } + + /** + * Returns the workflow + * + * TODO this is the default workflow, not yet the coresponding for the motion + */ + get workflow(): Workflow { + const motionsWorkflowConfig = this.DS.filter(Config, config => config.key === 'motions_workflow')[0] as Config; + //make sure this is a number + const workflowId = +motionsWorkflowConfig.value; + //get the workflow for our motion + const selectedWorkflow = this.DS.get(Workflow, workflowId) as Workflow; + return selectedWorkflow; + } + /** * return the workflow state * * Right now only the default workflow is assumed * TODO: Motion workflow needs to be specific on the server */ - get stateName() { - //get the default workflow - const motionsWorkflowConfig = this.DS.filter(Config, config => config.key === 'motions_workflow')[0] as Config; - //make sure this is a number - const workflowId = +motionsWorkflowConfig.value; - //get the workflow for out motion - const selectedWorkflow = this.DS.get(Workflow, workflowId) as Workflow; - const stateName = selectedWorkflow.getStateNameById(this.state_id); - if (stateName !== 'NULL') { - return stateName; - } else { - return ''; - } + get state() { + const workflow = this.workflow; + const state = workflow.state_by_id(this.state_id); + return state; } - get possibleStates() { - return ''; + /** + * returns possible states for the motion + */ + get possible_states(): WorkflowState[] { + return this.workflow.states; } + /** + * Returns possible "initial" states for a motion. + * + * Will filter "submitted" + */ + // get initial_states(): WorkflowState[] { + // const states = this.workflow.states; + + // //find index of 'submitted' + // const submitted = states.findIndex(state => state.name === 'submitted'); + + // //if found a valid index, remove "submitted" from array + // if (typeof submitted === 'number' && submitted >= 0) { + // states.splice(submitted, 1); + // } + + // return states; + // } + /** * Returns the name of the recommendation. * @@ -173,13 +208,10 @@ export class Motion extends BaseModel { * TODO: Motion workflow needs to be specific on the server */ get recommendation() { - //get the default workflow - const motionsWorkflowConfig = this.DS.filter(Config, config => config.key === 'motions_workflow')[0] as Config; - const workflowId = +motionsWorkflowConfig.value; - const selectedWorkflow = this.DS.get(Workflow, workflowId) as Workflow; - const stateName = selectedWorkflow.getStateNameById(this.recommendation_id); - if (stateName !== 'NULL') { - return stateName; + // const stateName = this.workflow.getStateNameById(this.recommendation_id); + const state = this.workflow.state_by_id(this.recommendation_id); + if (state) { + return state.recommendation_label; } else { return ''; } diff --git a/client/src/app/shared/models/motions/workflow-state.ts b/client/src/app/shared/models/motions/workflow-state.ts index 8cd689202..acc85b614 100644 --- a/client/src/app/shared/models/motions/workflow-state.ts +++ b/client/src/app/shared/models/motions/workflow-state.ts @@ -83,4 +83,8 @@ export class WorkflowState implements Deserializable { Object.assign(this, input); return this; } + + public toString = (): string => { + return this.name; + }; } diff --git a/client/src/app/shared/models/motions/workflow.ts b/client/src/app/shared/models/motions/workflow.ts index 0487fb07b..f1f8bb20f 100644 --- a/client/src/app/shared/models/motions/workflow.ts +++ b/client/src/app/shared/models/motions/workflow.ts @@ -21,14 +21,14 @@ export class Workflow extends BaseModel { this.first_state = first_state; } - getStateNameById(id: number): string { - let stateName = 'NULL'; + state_by_id(id: number): WorkflowState { + let targetState; this.states.forEach(state => { if (id === state.id) { - stateName = state.name; + targetState = state; } }); - return stateName; + return targetState as WorkflowState; } deserialize(input: any): this { diff --git a/client/src/app/shared/models/users/user.ts b/client/src/app/shared/models/users/user.ts index 31240e015..135f30412 100644 --- a/client/src/app/shared/models/users/user.ts +++ b/client/src/app/shared/models/users/user.ts @@ -64,4 +64,39 @@ export class User extends BaseModel { getGroups(): BaseModel | BaseModel[] { return this.DS.get('users/group', ...this.groups_id); } + + //TODO get full_name + + // TODO read config values for "users_sort_by" + get short_name(): string { + const title = this.title.trim(); + const firstName = this.first_name.trim(); + const lastName = this.last_name.trim(); + let shortName = ''; + + // TODO need DS adjustment first first + // if (this.DS.getConfig('users_sort_by').value === 'last_name') { + // if (lastName && firstName) { + // shortName += `${lastName}, ${firstName}`; + // } else { + // shortName += lastName || firstName; + // } + // } + + shortName += `${firstName} ${lastName}`; + + if (shortName.trim() === '') { + shortName = this.username; + } + + if (title) { + shortName = `${title} ${shortName}`; + } + + return shortName.trim(); + } + + public toString = (): string => { + return this.short_name; + }; } diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index 886a37795..f596c455f 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -20,6 +20,8 @@ import { MatDialogModule } from '@angular/material/dialog'; import { MatListModule } from '@angular/material/list'; import { MatExpansionModule } from '@angular/material/expansion'; import { MatMenuModule } from '@angular/material/menu'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; // FontAwesome modules import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; @@ -48,6 +50,8 @@ library.add(fas); imports: [ CommonModule, FormsModule, + MatFormFieldModule, + MatSelectModule, ReactiveFormsModule, MatButtonModule, MatCheckboxModule, @@ -68,6 +72,8 @@ library.add(fas); ], exports: [ FormsModule, + MatFormFieldModule, + MatSelectModule, ReactiveFormsModule, MatButtonModule, MatCheckboxModule, diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.html b/client/src/app/site/motions/motion-detail/motion-detail.component.html index 5437fb598..9ba5c33b7 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.html +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.html @@ -1,11 +1,11 @@ - - -
Motion {{motion.identifier}}: {{motion.currentTitle}}
@@ -16,37 +16,63 @@ - - + + + Meta information -
-
+ +
+ + +
+
+

Identifier

+ {{motion.identifier}} +
+ + + +
+ + +

Submitters

{{motion.submitterName}}
-
-

translate>Supporters

+ +
+

Supporters

-
-

- - Status - - -

- {{motion.stateName}} + +
+
+

State

+ {{motion.state.name}} +
+ + + {{state}} + + + + Reset State + + +
-
+ + + -
-

- - Category - - -

- {{motion.category}} -
- -
-

- Origin - -

- {{motion.origin}} - -
- - + +
+
+

Category

+ {{motion.category}} +
+ + + None + + {{cat}} +
-
-

Voting

+ +
+
+

Origin

+ {{motion.origin}} +
+ + +
-
+ + + + + - + + @@ -94,7 +126,9 @@ TEST - + + + @@ -102,11 +136,38 @@ -

The assembly may decide:

-
+
+ +
+
+

{{motion.currentTitle}}

+
+ + + +
+ + +

The assembly may decide:

+
+
+
+ + + + + +
+
+

Reason

+
+
+ + + +
+
-

Reason

-
@@ -117,8 +178,3 @@ - - - - - \ No newline at end of file diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.scss b/client/src/app/site/motions/motion-detail/motion-detail.component.scss index 9909432a8..8ab6f5fc2 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.scss +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.scss @@ -52,17 +52,33 @@ mat-panel-title { margin-left: 5px; } } +} - .meta-info-panel-body { +.wide-text { + width: 95%; + + textarea { + height: 200px; + } +} + +mat-expansion-panel { + .expansion-panel-custom-body { padding-left: 55px; } } .content-panel { + h2 { + display: block; + font-weight: bold; + font-size: 120%; + } + h3 { + display: block; font-weight: initial; font-size: 100%; - display: block; } h4 { diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.ts b/client/src/app/site/motions/motion-detail/motion-detail.component.ts index 409cb469c..08eefd9cb 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.ts +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.ts @@ -1,8 +1,10 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { OpenSlidesComponent } from '../../../openslides.component'; import { BaseComponent } from '../../../base.component'; import { Motion } from '../../../shared/models/motions/motion'; +import { Category } from '../../../shared/models/motions/category'; +import { FormGroup, FormBuilder } from '@angular/forms'; +import { MatExpansionPanel } from '@angular/material'; @Component({ selector: 'app-motion-detail', @@ -10,33 +12,73 @@ import { Motion } from '../../../shared/models/motions/motion'; styleUrls: ['./motion-detail.component.scss'] }) export class MotionDetailComponent extends BaseComponent implements OnInit { + @ViewChild('metaInfoPanel') metaInfoPanel: MatExpansionPanel; + @ViewChild('contentPanel') contentPanel: MatExpansionPanel; + motion: Motion; + metaInfoForm: FormGroup; + editMotion = false; - constructor(private route: ActivatedRoute) { + // categoryFormControl: FormControl; + + constructor(private route: ActivatedRoute, private formBuilder: FormBuilder) { super(); + this.createForm(); this.route.params.subscribe(params => { - console.log(params.id); - // has the motion of the DataStore was initialized before. - // Otherwise we need to observe DS this.motion = this.DS.get(Motion, params.id) as Motion; + if (this.motion) { + this.patchForm(); + } // Observe motion to get the motion in the parameter and also get the changes this.DS.getObservable().subscribe(newModel => { if (newModel instanceof Motion) { if (newModel.id === +params.id) { this.motion = newModel as Motion; - console.log('this.motion = ', this.motion); - // console.log('motion state name: ', this.motion.stateName); + this.patchForm(); } } }); }); } + /** Parches the Form with content from the dataStore */ + patchForm() { + this.metaInfoForm.patchValue({ categoryFormControl: this.motion.category }); + this.metaInfoForm.patchValue({ state: this.motion.state }); + } + + /** Create the whole Form with empty or default values */ + createForm() { + this.metaInfoForm = this.formBuilder.group({ + categoryFormControl: [''], + state: [''] + }); + } + + saveMotion() { + console.log('Save motion: ', this.metaInfoForm.value); + } + + getMotionCategories(): Category[] { + const categories = this.DS.get(Category); + return categories as Category[]; + } + + editMotionButton() { + this.editMotion ? (this.editMotion = false) : (this.editMotion = true); + if (this.editMotion) { + this.metaInfoPanel.open(); + this.contentPanel.open(); + } + + // console.log('this.motion.possible_states: ', this.motion.possible_states); + } + ngOnInit() { console.log('(init)the motion: ', this.motion); - console.log('motion state name: ', this.motion.stateName); + console.log('motion state name: ', this.motion.state); } downloadSingleMotionButton() { diff --git a/client/src/app/site/motions/motion-list/motion-list.component.html b/client/src/app/site/motions/motion-list/motion-list.component.html index 4a60dbbaf..3c0a34dda 100644 --- a/client/src/app/site/motions/motion-list/motion-list.component.html +++ b/client/src/app/site/motions/motion-list/motion-list.component.html @@ -53,8 +53,8 @@ State -
- +
+
@@ -63,4 +63,4 @@ - \ No newline at end of file + diff --git a/client/src/app/site/motions/motion-list/motion-list.component.ts b/client/src/app/site/motions/motion-list/motion-list.component.ts index 51200d068..364f96150 100644 --- a/client/src/app/site/motions/motion-list/motion-list.component.ts +++ b/client/src/app/site/motions/motion-list/motion-list.component.ts @@ -77,23 +77,24 @@ export class MotionListComponent extends BaseComponent implements OnInit { } selectMotion(motion) { - console.log('clicked a row, :', motion); - this.router.navigate(['./' + motion.id], { relativeTo: this.route }); } /** * Get the icon to the coresponding Motion Status * TODO Needs to be more accessible (Motion workflow needs adjustment on the server) - * @param stateName the name of the state + * @param state the name of the state */ - getStateIcon(stateName) { + getStateIcon(state) { + const stateName = state.name; if (stateName === 'accepted') { return 'thumbs-up'; } else if (stateName === 'rejected') { return 'thumbs-down'; } else if (stateName === 'not decided') { return 'question'; + } else { + return ''; } } diff --git a/client/src/app/site/site.component.ts b/client/src/app/site/site.component.ts index 9401e0daa..c1880146f 100644 --- a/client/src/app/site/site.component.ts +++ b/client/src/app/site/site.component.ts @@ -66,9 +66,9 @@ export class SiteComponent extends BaseComponent implements OnInit { }); //get a translation via code: use the translation service - this.translate.get('Motions').subscribe((res: string) => { - console.log('translation of motions in the target language: ' + res); - }); + // this.translate.get('Motions').subscribe((res: string) => { + // console.log('translation of motions in the target language: ' + res); + // }); //start autoupdate if the user is logged in: this.operator.whoAmI().subscribe(resp => { From d26d131fa60bb1110c31b9b4a17114ba09bf6302 Mon Sep 17 00:00:00 2001 From: Sean Engelhardt Date: Mon, 20 Aug 2018 12:28:43 +0200 Subject: [PATCH 2/6] Motion Workflow observing --- .../src/app/shared/models/motions/motion.ts | 78 +++++++++---------- .../motion-detail.component.html | 1 - .../motion-detail/motion-detail.component.ts | 12 +-- .../motion-list/motion-list.component.html | 2 +- client/src/app/site/start/start.component.ts | 1 - 5 files changed, 46 insertions(+), 48 deletions(-) diff --git a/client/src/app/shared/models/motions/motion.ts b/client/src/app/shared/models/motions/motion.ts index ef639c476..b6d443805 100644 --- a/client/src/app/shared/models/motions/motion.ts +++ b/client/src/app/shared/models/motions/motion.ts @@ -37,6 +37,11 @@ export class Motion extends BaseModel { agenda_item_id: number; log_messages: MotionLog[]; + // read from config + workflow_id: number; + // by the config above + workflow: Workflow; + constructor( id?: number, identifier?: string, @@ -79,6 +84,33 @@ export class Motion extends BaseModel { this.polls = polls; this.agenda_item_id = agenda_item_id; this.log_messages = log_messages; + + this.initDataStoreValues(); + } + + /** + * sets the workflow_id and the workflow + */ + initDataStoreValues() { + const motionsWorkflowConfig = this.DS.filter(Config, config => config.key === 'motions_workflow')[0] as Config; + if (motionsWorkflowConfig) { + this.workflow_id = +motionsWorkflowConfig.value; + } else { + this.DS.getObservable().subscribe(newConfig => { + if (newConfig instanceof Config && newConfig.key === 'motions_workflow') { + this.workflow_id = +newConfig.value; + } + }); + } + + this.workflow = this.DS.get(Workflow, this.workflow_id) as Workflow; + if (!this.workflow.id) { + this.DS.getObservable().subscribe(newModel => { + if (newModel instanceof Workflow && newModel.id === this.workflow_id) { + this.workflow = newModel; + } + }); + } } /** @@ -149,30 +181,18 @@ export class Motion extends BaseModel { this.category_id = newCategory.id; } - /** - * Returns the workflow - * - * TODO this is the default workflow, not yet the coresponding for the motion - */ - get workflow(): Workflow { - const motionsWorkflowConfig = this.DS.filter(Config, config => config.key === 'motions_workflow')[0] as Config; - //make sure this is a number - const workflowId = +motionsWorkflowConfig.value; - //get the workflow for our motion - const selectedWorkflow = this.DS.get(Workflow, workflowId) as Workflow; - return selectedWorkflow; - } - /** * return the workflow state * - * Right now only the default workflow is assumed - * TODO: Motion workflow needs to be specific on the server + * set the workflow via a component */ get state() { - const workflow = this.workflow; - const state = workflow.state_by_id(this.state_id); - return state; + if (this.workflow && this.workflow.id) { + const state = this.workflow.state_by_id(this.state_id); + return state; + } else { + return null; + } } /** @@ -182,25 +202,6 @@ export class Motion extends BaseModel { return this.workflow.states; } - /** - * Returns possible "initial" states for a motion. - * - * Will filter "submitted" - */ - // get initial_states(): WorkflowState[] { - // const states = this.workflow.states; - - // //find index of 'submitted' - // const submitted = states.findIndex(state => state.name === 'submitted'); - - // //if found a valid index, remove "submitted" from array - // if (typeof submitted === 'number' && submitted >= 0) { - // states.splice(submitted, 1); - // } - - // return states; - // } - /** * Returns the name of the recommendation. * @@ -208,7 +209,6 @@ export class Motion extends BaseModel { * TODO: Motion workflow needs to be specific on the server */ get recommendation() { - // const stateName = this.workflow.getStateNameById(this.recommendation_id); const state = this.workflow.state_by_id(this.recommendation_id); if (state) { return state.recommendation_label; diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.html b/client/src/app/site/motions/motion-detail/motion-detail.component.html index 9ba5c33b7..8cf439405 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.html +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.html @@ -113,7 +113,6 @@

Voting

--> -
diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.ts b/client/src/app/site/motions/motion-detail/motion-detail.component.ts index 08eefd9cb..3c35971cc 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.ts +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.ts @@ -27,16 +27,12 @@ export class MotionDetailComponent extends BaseComponent implements OnInit { this.route.params.subscribe(params => { // has the motion of the DataStore was initialized before. this.motion = this.DS.get(Motion, params.id) as Motion; - if (this.motion) { - this.patchForm(); - } // Observe motion to get the motion in the parameter and also get the changes this.DS.getObservable().subscribe(newModel => { if (newModel instanceof Motion) { if (newModel.id === +params.id) { this.motion = newModel as Motion; - this.patchForm(); } } }); @@ -68,12 +64,16 @@ export class MotionDetailComponent extends BaseComponent implements OnInit { editMotionButton() { this.editMotion ? (this.editMotion = false) : (this.editMotion = true); + if (this.editMotion) { + // switch to edit mode + this.patchForm(); this.metaInfoPanel.open(); this.contentPanel.open(); + } else { + // save button + this.saveMotion(); } - - // console.log('this.motion.possible_states: ', this.motion.possible_states); } ngOnInit() { diff --git a/client/src/app/site/motions/motion-list/motion-list.component.html b/client/src/app/site/motions/motion-list/motion-list.component.html index 3c0a34dda..5bde0f84c 100644 --- a/client/src/app/site/motions/motion-list/motion-list.component.html +++ b/client/src/app/site/motions/motion-list/motion-list.component.html @@ -53,7 +53,7 @@ State -
+
diff --git a/client/src/app/site/start/start.component.ts b/client/src/app/site/start/start.component.ts index fcf6abb50..cc4db77d3 100644 --- a/client/src/app/site/start/start.component.ts +++ b/client/src/app/site/start/start.component.ts @@ -64,7 +64,6 @@ export class StartComponent extends BaseComponent implements OnInit { if (welcomeTextConfig) { this.welcomeText = welcomeTextConfig.value as string; } - console.log(this.DS.filter(Config, config => config.key === 'general_event_welcome_title')); // observe title and text in DS this.DS.getObservable().subscribe(newModel => { From b808228b426f6ea6bbab7da5135655906506f805 Mon Sep 17 00:00:00 2001 From: sean Date: Mon, 20 Aug 2018 18:13:28 +0200 Subject: [PATCH 3/6] Save updated motion in DataStore, reactive form --- .../src/app/shared/models/motions/motion.ts | 34 +++++++-- .../motion-detail.component.html | 74 +++++++++++-------- .../motion-detail.component.scss | 4 + .../motion-detail/motion-detail.component.ts | 32 ++++++-- 4 files changed, 98 insertions(+), 46 deletions(-) diff --git a/client/src/app/shared/models/motions/motion.ts b/client/src/app/shared/models/motions/motion.ts index b6d443805..5ac7aae8e 100644 --- a/client/src/app/shared/models/motions/motion.ts +++ b/client/src/app/shared/models/motions/motion.ts @@ -88,6 +88,13 @@ export class Motion extends BaseModel { this.initDataStoreValues(); } + /** + * update the values of the motion with new values + */ + patchValues(update: object) { + Object.assign(this, update); + } + /** * sets the workflow_id and the workflow */ @@ -116,7 +123,7 @@ export class Motion extends BaseModel { /** * returns the most current title from versions */ - get currentTitle() { + get currentTitle(): string { if (this.versions[0]) { return this.versions[0].title; } else { @@ -124,6 +131,12 @@ export class Motion extends BaseModel { } } + set currentTitle(newTitle: string) { + if (this.versions[0]) { + this.versions[0].title = newTitle; + } + } + /** * returns the most current motion text from versions */ @@ -131,6 +144,10 @@ export class Motion extends BaseModel { return this.versions[0].text; } + set currentText(newText: string) { + this.versions[0].text = newText; + } + /** * returns the most current motion reason text from versions */ @@ -138,6 +155,10 @@ export class Motion extends BaseModel { return this.versions[0].reason; } + set currentReason(newReason: string) { + this.versions[0].reason = newReason; + } + /** * return the submitters as uses objects */ @@ -183,8 +204,6 @@ export class Motion extends BaseModel { /** * return the workflow state - * - * set the workflow via a component */ get state() { if (this.workflow && this.workflow.id) { @@ -205,15 +224,14 @@ export class Motion extends BaseModel { /** * Returns the name of the recommendation. * - * Right now only the default workflow is assumed * TODO: Motion workflow needs to be specific on the server */ get recommendation() { - const state = this.workflow.state_by_id(this.recommendation_id); - if (state) { - return state.recommendation_label; + if (this.workflow && this.workflow.id) { + const state = this.workflow.state_by_id(this.recommendation_id); + return state; } else { - return ''; + return null; } } diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.html b/client/src/app/site/motions/motion-detail/motion-detail.component.html index 8cf439405..af468d04a 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.html +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.html @@ -1,13 +1,17 @@ -
- Motion {{motion.identifier}}: {{motion.currentTitle}} + Motion + {{motion.identifier}} + {{metaInfoForm.get('identifier').value}} + : + {{motion.currentTitle}} + {{contentForm.get('currentTitle').value}}
by {{motion.submitterName}} @@ -18,7 +22,8 @@ - + + @@ -36,7 +41,7 @@ {{motion.identifier}}
- +
@@ -59,27 +64,40 @@ {{motion.state.name}}
- - {{state}} + + {{state}} - Reset State + Reset State
- -
-

+ +
+
+

{{motion.recomBy}}

+ {{motion.recommendation.name}} +
+ + + {{state}} + + + + Reset recommendation + + + + +
@@ -89,10 +107,10 @@ {{motion.category}}

- + None - {{cat}} + {{cat}}
@@ -104,7 +122,7 @@ {{motion.origin}}
- +
@@ -135,24 +153,26 @@ -
+
+

{{motion.currentTitle}}

- +
+

The assembly may decide:

- + @@ -162,18 +182,10 @@
- +
-
- +
-
- - - - - - - + \ No newline at end of file diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.scss b/client/src/app/site/motions/motion-detail/motion-detail.component.scss index 8ab6f5fc2..181e831b8 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.scss +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.scss @@ -2,6 +2,10 @@ span { margin: 0; } +.save-button { + background-color: rgb(77, 243, 86); +} + .motion-title { padding-left: 20px; line-height: 100%; diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.ts b/client/src/app/site/motions/motion-detail/motion-detail.component.ts index 3c35971cc..e06c3356b 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.ts +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.ts @@ -17,10 +17,9 @@ export class MotionDetailComponent extends BaseComponent implements OnInit { motion: Motion; metaInfoForm: FormGroup; + contentForm: FormGroup; editMotion = false; - // categoryFormControl: FormControl; - constructor(private route: ActivatedRoute, private formBuilder: FormBuilder) { super(); this.createForm(); @@ -41,20 +40,39 @@ export class MotionDetailComponent extends BaseComponent implements OnInit { /** Parches the Form with content from the dataStore */ patchForm() { - this.metaInfoForm.patchValue({ categoryFormControl: this.motion.category }); - this.metaInfoForm.patchValue({ state: this.motion.state }); + this.metaInfoForm.patchValue({ + category_id: this.motion.category.id, + state_id: this.motion.state.id, + recommendation_id: this.motion.recommendation.id, + identifier: this.motion.identifier, + origin: this.motion.origin + }); + this.contentForm.patchValue({ + currentTitle: this.motion.currentTitle, + currentText: this.motion.currentText, + currentReason: this.motion.currentReason + }); } /** Create the whole Form with empty or default values */ createForm() { this.metaInfoForm = this.formBuilder.group({ - categoryFormControl: [''], - state: [''] + identifier: [''], + category_id: [''], + state_id: [''], + recommendation_id: [''], + origin: [''] + }); + this.contentForm = this.formBuilder.group({ + currentTitle: [''], + currentText: [''], + currentReason: [''] }); } saveMotion() { - console.log('Save motion: ', this.metaInfoForm.value); + const newMotionValues = { ...this.metaInfoForm.value, ...this.contentForm.value }; + this.motion.patchValues(newMotionValues); } getMotionCategories(): Category[] { From 133ecb4724cd6f33186e930e901bae006dece3dd Mon Sep 17 00:00:00 2001 From: Sean Engelhardt Date: Tue, 21 Aug 2018 14:56:26 +0200 Subject: [PATCH 4/6] add new Motion form --- .../app/core/services/dataStore.service.ts | 6 +- .../shared/models/motions/motion-version.ts | 8 +- .../src/app/shared/models/motions/motion.ts | 73 +++++++++++------ .../motion-detail.component.html | 21 ++--- .../motion-detail/motion-detail.component.ts | 82 +++++++++++++++---- .../motion-list/motion-list.component.html | 4 +- .../motion-list/motion-list.component.ts | 6 +- .../site/motions/motions-routing.module.ts | 2 +- client/src/app/site/start/start.component.ts | 3 +- 9 files changed, 136 insertions(+), 69 deletions(-) diff --git a/client/src/app/core/services/dataStore.service.ts b/client/src/app/core/services/dataStore.service.ts index 3183a5be2..1ae5ea506 100644 --- a/client/src/app/core/services/dataStore.service.ts +++ b/client/src/app/core/services/dataStore.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse, HttpErrorResponse, HttpHeaders } from '@angular/common/http'; -import { Observable, of, BehaviorSubject } from 'rxjs'; -import { tap, map } from 'rxjs/operators'; +import { HttpClient } from '@angular/common/http'; +import { Observable, BehaviorSubject } from 'rxjs'; +import { tap } from 'rxjs/operators'; import { ImproperlyConfiguredError } from 'app/core/exceptions'; import { BaseModel, ModelId } from 'app/shared/models/base.model'; diff --git a/client/src/app/shared/models/motions/motion-version.ts b/client/src/app/shared/models/motions/motion-version.ts index 522d6c84f..171b26f5d 100644 --- a/client/src/app/shared/models/motions/motion-version.ts +++ b/client/src/app/shared/models/motions/motion-version.ts @@ -26,10 +26,10 @@ export class MotionVersion implements Deserializable { this.id = id; this.version_number = version_number; this.creation_time = creation_time; - this.title = title; - this.text = text; - this.amendment_paragraphs = amendment_paragraphs; - this.reason = reason; + this.title = title || ''; + this.text = text || ''; + this.amendment_paragraphs = amendment_paragraphs || ''; + this.reason = reason || ''; } deserialize(input: any): this { diff --git a/client/src/app/shared/models/motions/motion.ts b/client/src/app/shared/models/motions/motion.ts index 5ac7aae8e..ff7b39322 100644 --- a/client/src/app/shared/models/motions/motion.ts +++ b/client/src/app/shared/models/motions/motion.ts @@ -66,24 +66,24 @@ export class Motion extends BaseModel { super(); this._collectionString = 'motions/motion'; this.id = id; - this.identifier = identifier; - this.versions = versions; + this.identifier = identifier || ''; + this.versions = versions || [new MotionVersion()]; this.active_version = active_version; this.parent_id = parent_id; this.category_id = category_id; this.motion_block_id = motion_block_id; - this.origin = origin; - this.submitters = submitters; + this.origin = origin || ''; + this.submitters = submitters || [new MotionSubmitter()]; this.supporters_id = supporters_id; this.comments = comments; this.state_id = state_id; - this.state_required_permission_to_see = state_required_permission_to_see; + this.state_required_permission_to_see = state_required_permission_to_see || ''; this.recommendation_id = recommendation_id; this.tags_id = tags_id; this.attachments_id = attachments_id; this.polls = polls; this.agenda_item_id = agenda_item_id; - this.log_messages = log_messages; + this.log_messages = log_messages || [new MotionLog()]; this.initDataStoreValues(); } @@ -124,7 +124,7 @@ export class Motion extends BaseModel { * returns the most current title from versions */ get currentTitle(): string { - if (this.versions[0]) { + if (this.versions && this.versions[0]) { return this.versions[0].title; } else { return ''; @@ -141,7 +141,11 @@ export class Motion extends BaseModel { * returns the most current motion text from versions */ get currentText() { - return this.versions[0].text; + if (this.versions) { + return this.versions[0].text; + } else { + return null; + } } set currentText(newText: string) { @@ -152,9 +156,17 @@ export class Motion extends BaseModel { * returns the most current motion reason text from versions */ get currentReason() { - return this.versions[0].reason; + if (this.versions) { + return this.versions[0].reason; + } else { + return null; + } } + /** + * Update the current reason. + * TODO: ignores motion versions. Should make a new one. + */ set currentReason(newReason: string) { this.versions[0].reason = newReason; } @@ -164,20 +176,24 @@ export class Motion extends BaseModel { */ get submitterAsUser() { const submitterIds = []; - this.submitters.forEach(submitter => { - submitterIds.push(submitter.user_id); - }); - const users = this.DS.get(User, ...submitterIds); - return users; + if (this.submitters) { + this.submitters.forEach(submitter => { + submitterIds.push(submitter.user_id); + }); + const users = this.DS.get(User, ...submitterIds); + return users; + } else { + return null; + } } /** * returns the name of the first submitter */ get submitterName() { - const mainSubmitter = this.DS.get(User, this.submitters[0].user_id) as User; - if (mainSubmitter) { - return mainSubmitter; + const submitters = this.submitterAsUser; + if (submitters) { + return submitters[0]; } else { return ''; } @@ -191,7 +207,7 @@ export class Motion extends BaseModel { const motionCategory = this.DS.get(Category, this.category_id); return motionCategory as Category; } else { - return 'none'; + return ''; } } @@ -205,12 +221,12 @@ export class Motion extends BaseModel { /** * return the workflow state */ - get state() { - if (this.workflow && this.workflow.id) { + get state(): any { + if (this.state_id && this.workflow && this.workflow.id) { const state = this.workflow.state_by_id(this.state_id); return state; } else { - return null; + return ''; } } @@ -226,12 +242,12 @@ export class Motion extends BaseModel { * * TODO: Motion workflow needs to be specific on the server */ - get recommendation() { - if (this.workflow && this.workflow.id) { + get recommendation(): any { + if (this.recommendation_id && this.workflow && this.workflow.id) { const state = this.workflow.state_by_id(this.recommendation_id); return state; } else { - return null; + return ''; } } @@ -243,8 +259,13 @@ export class Motion extends BaseModel { Config, config => config.key === 'motions_recommendations_by' )[0] as Config; - const recomByString = motionsRecommendationsByConfig.value; - return recomByString; + + if (motionsRecommendationsByConfig) { + const recomByString = motionsRecommendationsByConfig.value; + return recomByString; + } else { + return null; + } } deserialize(input: any): this { diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.html b/client/src/app/site/motions/motion-detail/motion-detail.component.html index af468d04a..482e3c543 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.html +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.html @@ -1,14 +1,16 @@ -
+ New Motion {{motion.identifier}} - {{metaInfoForm.get('identifier').value}} + {{metaInfoForm.get('identifier').value}} : {{motion.currentTitle}} {{contentForm.get('currentTitle').value}} @@ -23,7 +25,7 @@ - + @@ -35,7 +37,8 @@
-
+
+

Identifier

{{motion.identifier}} @@ -92,12 +95,6 @@ - -
@@ -185,7 +182,7 @@
- + - \ No newline at end of file + diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.ts b/client/src/app/site/motions/motion-detail/motion-detail.component.ts index e06c3356b..d000e1a4e 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.ts +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.ts @@ -19,26 +19,46 @@ export class MotionDetailComponent extends BaseComponent implements OnInit { metaInfoForm: FormGroup; contentForm: FormGroup; editMotion = false; + newMotion = false; + /** + * + * @param route determine if this is a new or an existing motion + * @param formBuilder For reactive forms. Form Group and Form Control + */ constructor(private route: ActivatedRoute, private formBuilder: FormBuilder) { super(); this.createForm(); - this.route.params.subscribe(params => { - // has the motion of the DataStore was initialized before. - this.motion = this.DS.get(Motion, params.id) as Motion; - // Observe motion to get the motion in the parameter and also get the changes - this.DS.getObservable().subscribe(newModel => { - if (newModel instanceof Motion) { - if (newModel.id === +params.id) { - this.motion = newModel as Motion; + console.log('route: ', route.snapshot.url[0].path); + + if (route.snapshot.url[0].path === 'new') { + this.newMotion = true; + this.editMotion = true; + this.motion = new Motion(); + } else { + // load existing motion + this.route.params.subscribe(params => { + console.log('params ', params); + + // has the motion of the DataStore was initialized before. + this.motion = this.DS.get(Motion, params.id) as Motion; + + // Observe motion to get the motion in the parameter and also get the changes + this.DS.getObservable().subscribe(newModel => { + if (newModel instanceof Motion) { + if (newModel.id === +params.id) { + this.motion = newModel as Motion; + } } - } + }); }); - }); + } } - /** Parches the Form with content from the dataStore */ + /** + * Async load the values of the motion in the Form. + */ patchForm() { this.metaInfoForm.patchValue({ category_id: this.motion.category.id, @@ -54,7 +74,11 @@ export class MotionDetailComponent extends BaseComponent implements OnInit { }); } - /** Create the whole Form with empty or default values */ + /** + * Creates the forms for the Motion and the MotionVersion + * + * TODO: Build a custom form validator + */ createForm() { this.metaInfoForm = this.formBuilder.group({ identifier: [''], @@ -70,35 +94,57 @@ export class MotionDetailComponent extends BaseComponent implements OnInit { }); } + /** + * Save a motion. Calls the "patchValues" function in the MotionObject + * + * http:post the motion to the server. + * The AutoUpdate-Service should see a change once it arrives and show it + * in the list view automatically + */ saveMotion() { const newMotionValues = { ...this.metaInfoForm.value, ...this.contentForm.value }; this.motion.patchValues(newMotionValues); + + console.log('save motion: this: ', this); + + this.DS.save(this.motion).subscribe(answer => { + console.log(answer); + }); } + /** + * return all Categories. + */ getMotionCategories(): Category[] { const categories = this.DS.get(Category); return categories as Category[]; } + /** + * Click on the edit button (pen-symbol) + */ editMotionButton() { this.editMotion ? (this.editMotion = false) : (this.editMotion = true); if (this.editMotion) { - // switch to edit mode this.patchForm(); this.metaInfoPanel.open(); this.contentPanel.open(); } else { - // save button this.saveMotion(); } } - ngOnInit() { - console.log('(init)the motion: ', this.motion); - console.log('motion state name: ', this.motion.state); - } + /** + * Init. Does nothing here. + */ + ngOnInit() {} + /** + * Function to download a motion. + * + * TODO: does nothing yet. + */ downloadSingleMotionButton() { console.log('Download this motion'); } diff --git a/client/src/app/site/motions/motion-list/motion-list.component.html b/client/src/app/site/motions/motion-list/motion-list.component.html index 5bde0f84c..9a2868567 100644 --- a/client/src/app/site/motions/motion-list/motion-list.component.html +++ b/client/src/app/site/motions/motion-list/motion-list.component.html @@ -1,6 +1,6 @@ - @@ -43,7 +43,7 @@
by - {{motion.submitterAsUser.username}} + {{motion.submitterAsUser}}
diff --git a/client/src/app/site/motions/motion-list/motion-list.component.ts b/client/src/app/site/motions/motion-list/motion-list.component.ts index 364f96150..8e9903a7a 100644 --- a/client/src/app/site/motions/motion-list/motion-list.component.ts +++ b/client/src/app/site/motions/motion-list/motion-list.component.ts @@ -70,8 +70,10 @@ export class MotionListComponent extends BaseComponent implements OnInit { // The alternative approach is to put the observable as DataSource to the table this.DS.getObservable().subscribe(newModel => { if (newModel instanceof Motion) { - this.motionArray.push(newModel as Motion); - this.dataSource.data = this.motionArray; + if (!this.motionArray.includes(newModel)) { + this.motionArray.push(newModel as Motion); + this.dataSource.data = this.motionArray; + } } }); } diff --git a/client/src/app/site/motions/motions-routing.module.ts b/client/src/app/site/motions/motions-routing.module.ts index e85a13e36..f74ec93e0 100644 --- a/client/src/app/site/motions/motions-routing.module.ts +++ b/client/src/app/site/motions/motions-routing.module.ts @@ -5,7 +5,7 @@ import { MotionDetailComponent } from './motion-detail/motion-detail.component'; const routes: Routes = [ { path: '', component: MotionListComponent }, - { path: 'dummy', component: MotionDetailComponent }, + { path: 'new', component: MotionDetailComponent }, { path: ':id', component: MotionDetailComponent } ]; diff --git a/client/src/app/site/start/start.component.ts b/client/src/app/site/start/start.component.ts index cc4db77d3..6030d5b84 100644 --- a/client/src/app/site/start/start.component.ts +++ b/client/src/app/site/start/start.component.ts @@ -111,7 +111,8 @@ export class StartComponent extends BaseComponent implements OnInit { * function to print datastore */ giveDataStore() { - this.DS.printWhole(); + // this.DS.printWhole(); + console.log('only the motions: \n', this.DS.get(Motion) as Motion[]); } /** From de61505b0008ba33a113e3795125d4c4a578dc31 Mon Sep 17 00:00:00 2001 From: Sean Engelhardt Date: Wed, 22 Aug 2018 11:26:53 +0200 Subject: [PATCH 5/6] Put/Post motions to server Temporarily over dataStore, will need own service --- .../app/core/services/dataStore.service.ts | 38 +++++++--- .../src/app/core/services/operator.service.ts | 24 ++++++- client/src/app/openslides.component.ts | 1 + .../src/app/shared/models/motions/motion.ts | 29 ++++---- .../motion-detail.component.html | 30 ++++---- .../motion-detail/motion-detail.component.ts | 70 +++++++++++++++---- .../motion-list/motion-list.component.ts | 33 +++++++-- client/src/app/site/start/start.component.ts | 3 +- 8 files changed, 166 insertions(+), 62 deletions(-) diff --git a/client/src/app/core/services/dataStore.service.ts b/client/src/app/core/services/dataStore.service.ts index 1ae5ea506..29af1e678 100644 --- a/client/src/app/core/services/dataStore.service.ts +++ b/client/src/app/core/services/dataStore.service.ts @@ -29,6 +29,12 @@ interface Storage { * Use this.DS in an OpenSlides Component to Access the store. * Used by a lot of components, classes and services. * Changes can be observed + * + * FIXME: The injector does not init the HttpClient Service. + * Either remove it from DataStore and make an own Service + * fix it somehow + * or just do-not let the OpenSlidesComponent inject DataStore to it's + * children. */ @Injectable({ providedIn: 'root' @@ -49,7 +55,9 @@ export class DataStoreService { * Empty constructor for dataStore * @param http use HttpClient to send models back to the server */ - constructor(private http: HttpClient) {} + constructor(private http: HttpClient) { + console.log('constructor of dataStore. http: ', this.http); + } /** * Read one, multiple or all ID's from dataStore @@ -157,21 +165,29 @@ export class DataStoreService { /** * Saves the given model on the server - * @param model the BaseModel that shall be removed + * @param model the BaseModel that shall be saved * @return Observable of BaseModel */ save(model: BaseModel): Observable { if (!model.id) { - throw new ImproperlyConfiguredError('The model must have an id!'); + return this.http.post('rest/' + model.collectionString + '/', model).pipe( + tap( + response => { + console.log('New Model added. Response : ', response); + }, + error => console.log('error. ', error) + ) + ); + } else { + return this.http.put('rest/' + model.collectionString + '/' + model.id, model).pipe( + tap( + response => { + console.log('Update model. Response : ', response); + }, + error => console.log('error. ', error) + ) + ); } - - // TODO not tested - return this.http.post(model.collectionString + '/', model).pipe( - tap(response => { - console.log('the response: ', response); - this.add(model); - }) - ); } /** diff --git a/client/src/app/core/services/operator.service.ts b/client/src/app/core/services/operator.service.ts index 9655a8b17..30d25ee31 100644 --- a/client/src/app/core/services/operator.service.ts +++ b/client/src/app/core/services/operator.service.ts @@ -4,6 +4,7 @@ import { HttpClient } from '@angular/common/http'; import { tap, catchError, share } from 'rxjs/operators'; import { OpenSlidesComponent } from 'app/openslides.component'; import { Group } from 'app/shared/models/users/group'; +import { User } from '../../shared/models/users/user'; /** * The operator represents the user who is using OpenSlides. @@ -38,6 +39,8 @@ export class OperatorService extends OpenSlidesComponent { username: string; logged_in: boolean; + private _user: User; + /** * The subject that can be observed by other instances using observing functions. */ @@ -65,7 +68,7 @@ export class OperatorService extends OpenSlidesComponent { } } - // observe the datastore now to avoid race conditions. Ensures to + // observe the DataStore now to avoid race conditions. Ensures to // find the groups in time this.observeDataStore(); } @@ -77,7 +80,7 @@ export class OperatorService extends OpenSlidesComponent { return this.http.get('/users/whoami/').pipe( tap(whoami => { if (whoami && whoami.user) { - this.storeUser(whoami.user); + this.storeUser(whoami.user as User); } }), catchError(this.handleError()) @@ -87,8 +90,11 @@ export class OperatorService extends OpenSlidesComponent { /** * Store the user Information in the operator, the localStorage and update the Observable * @param user usually a http response that represents a user. + * + * Todo: Could be refractored to use the actual User Object. + * Operator is older than user, so this is still a traditional JS way */ - public storeUser(user: any): void { + public storeUser(user: User): void { // store in file this.about_me = user.about_me; this.comment = user.comment; @@ -106,6 +112,7 @@ export class OperatorService extends OpenSlidesComponent { this.structure_level = user.structure_level; this.title = user.title; this.username = user.username; + // also store in localstorrage this.updateLocalStorage(); // update mode to inform observers @@ -187,6 +194,10 @@ export class OperatorService extends OpenSlidesComponent { if (newModel instanceof Group) { this.addGroup(newModel); } + + if (newModel instanceof User && this.id === newModel.id) { + this._user = newModel; + } }); } @@ -243,4 +254,11 @@ export class OperatorService extends OpenSlidesComponent { this.setObservable(newGroup); } } + + /** + * get the user that corresponds to operator. + */ + get user(): User { + return this._user; + } } diff --git a/client/src/app/openslides.component.ts b/client/src/app/openslides.component.ts index 951646758..570684bfb 100644 --- a/client/src/app/openslides.component.ts +++ b/client/src/app/openslides.component.ts @@ -1,4 +1,5 @@ import { Injector } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; import { Observable, of } from 'rxjs'; import { DataStoreService } from 'app/core/services/dataStore.service'; diff --git a/client/src/app/shared/models/motions/motion.ts b/client/src/app/shared/models/motions/motion.ts index ff7b39322..f78e69542 100644 --- a/client/src/app/shared/models/motions/motion.ts +++ b/client/src/app/shared/models/motions/motion.ts @@ -42,6 +42,10 @@ export class Motion extends BaseModel { // by the config above workflow: Workflow; + // for request + title: string; + text: string; + constructor( id?: number, identifier?: string, @@ -73,7 +77,7 @@ export class Motion extends BaseModel { this.category_id = category_id; this.motion_block_id = motion_block_id; this.origin = origin || ''; - this.submitters = submitters || [new MotionSubmitter()]; + this.submitters = submitters || []; this.supporters_id = supporters_id; this.comments = comments; this.state_id = state_id; @@ -83,7 +87,7 @@ export class Motion extends BaseModel { this.attachments_id = attachments_id; this.polls = polls; this.agenda_item_id = agenda_item_id; - this.log_messages = log_messages || [new MotionLog()]; + this.log_messages = log_messages || []; this.initDataStoreValues(); } @@ -120,6 +124,13 @@ export class Motion extends BaseModel { } } + /** add a new motionSubmitter from user-object */ + addSubmitter(user: User) { + const newSubmitter = new MotionSubmitter(null, user.id); + this.submitters.push(newSubmitter); + console.log('did addSubmitter. this.submitters: ', this.submitters); + } + /** * returns the most current title from versions */ @@ -176,7 +187,7 @@ export class Motion extends BaseModel { */ get submitterAsUser() { const submitterIds = []; - if (this.submitters) { + if (this.submitters && this.submitters.length > 0) { this.submitters.forEach(submitter => { submitterIds.push(submitter.user_id); }); @@ -187,18 +198,6 @@ export class Motion extends BaseModel { } } - /** - * returns the name of the first submitter - */ - get submitterName() { - const submitters = this.submitterAsUser; - if (submitters) { - return submitters[0]; - } else { - return ''; - } - } - /** * get the category of a motion as object */ diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.html b/client/src/app/site/motions/motion-detail/motion-detail.component.html index 482e3c543..6a4d9bc26 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.html +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.html @@ -9,14 +9,14 @@
New Motion - {{motion.identifier}} + {{motion.identifier}} {{metaInfoForm.get('identifier').value}} : - {{motion.currentTitle}} + {{motion.currentTitle}} {{contentForm.get('currentTitle').value}}
-
- by {{motion.submitterName}} +
+ by {{motion.submitterAsUser}}
@@ -49,19 +49,19 @@
-
+

Submitters

- {{motion.submitterName}} + {{motion.submitterAsUser}}
-
+

Supporters

-
+

State

{{motion.state.name}} @@ -80,7 +80,7 @@ -
+

{{motion.recomBy}}

{{motion.recommendation.name}} @@ -98,7 +98,7 @@
-
+

Category

{{motion.category}} @@ -113,7 +113,7 @@
-
+

Origin

{{motion.origin}} @@ -153,7 +153,7 @@
-
+

{{motion.currentTitle}}

@@ -165,15 +165,15 @@

The assembly may decide:

-
+
- + -
+

Reason

diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.ts b/client/src/app/site/motions/motion-detail/motion-detail.component.ts index d000e1a4e..df8b8bf89 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.ts +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.ts @@ -1,37 +1,78 @@ import { Component, OnInit, ViewChild } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { BaseComponent } from '../../../base.component'; import { Motion } from '../../../shared/models/motions/motion'; import { Category } from '../../../shared/models/motions/category'; import { FormGroup, FormBuilder } from '@angular/forms'; import { MatExpansionPanel } from '@angular/material'; +import { DataStoreService } from '../../../core/services/dataStore.service'; +import { OperatorService } from '../../../core/services/operator.service'; +/** + * Component for the motion detail view + */ @Component({ selector: 'app-motion-detail', templateUrl: './motion-detail.component.html', styleUrls: ['./motion-detail.component.scss'] }) -export class MotionDetailComponent extends BaseComponent implements OnInit { +// export class MotionDetailComponent extends BaseComponent implements OnInit { +export class MotionDetailComponent implements OnInit { + /** + * MatExpansionPanel for the meta info + */ @ViewChild('metaInfoPanel') metaInfoPanel: MatExpansionPanel; + + /** + * MatExpansionPanel for the content panel + */ @ViewChild('contentPanel') contentPanel: MatExpansionPanel; + /** + * Target motion. Might be new or old + */ motion: Motion; + + /** + * Motions meta-info + */ metaInfoForm: FormGroup; + + /** + * Motion content. Can be a new version + */ contentForm: FormGroup; + + /** + * Determine if the motion is edited + */ editMotion = false; + + /** + * Determine if the motion is new + */ newMotion = false; /** + * Constuct the detail view. + * + * TODO: DataStore needs removed and added via the parent. + * Own service for put and post required * * @param route determine if this is a new or an existing motion * @param formBuilder For reactive forms. Form Group and Form Control */ - constructor(private route: ActivatedRoute, private formBuilder: FormBuilder) { - super(); + constructor( + private router: Router, + private route: ActivatedRoute, + private formBuilder: FormBuilder, + private operator: OperatorService, + private myDataStore: DataStoreService + ) { + // TODO: Add super again + // super(); this.createForm(); - console.log('route: ', route.snapshot.url[0].path); - if (route.snapshot.url[0].path === 'new') { this.newMotion = true; this.editMotion = true; @@ -42,10 +83,10 @@ export class MotionDetailComponent extends BaseComponent implements OnInit { console.log('params ', params); // has the motion of the DataStore was initialized before. - this.motion = this.DS.get(Motion, params.id) as Motion; + this.motion = this.myDataStore.get(Motion, params.id) as Motion; // Observe motion to get the motion in the parameter and also get the changes - this.DS.getObservable().subscribe(newModel => { + this.myDataStore.getObservable().subscribe(newModel => { if (newModel instanceof Motion) { if (newModel.id === +params.id) { this.motion = newModel as Motion; @@ -105,10 +146,15 @@ export class MotionDetailComponent extends BaseComponent implements OnInit { const newMotionValues = { ...this.metaInfoForm.value, ...this.contentForm.value }; this.motion.patchValues(newMotionValues); - console.log('save motion: this: ', this); + // TODO: This is DRAFT. Reads out Motion version directly. Potentially insecure. + this.motion.title = this.motion.currentTitle; + this.motion.text = this.motion.currentText; - this.DS.save(this.motion).subscribe(answer => { - console.log(answer); + this.myDataStore.save(this.motion).subscribe(answer => { + console.log('answer, ', answer); + if (answer && answer.id && this.newMotion) { + this.router.navigate(['./motions/' + answer.id]); + } }); } @@ -116,7 +162,7 @@ export class MotionDetailComponent extends BaseComponent implements OnInit { * return all Categories. */ getMotionCategories(): Category[] { - const categories = this.DS.get(Category); + const categories = this.myDataStore.get(Category); return categories as Category[]; } diff --git a/client/src/app/site/motions/motion-list/motion-list.component.ts b/client/src/app/site/motions/motion-list/motion-list.component.ts index 8e9903a7a..820a25602 100644 --- a/client/src/app/site/motions/motion-list/motion-list.component.ts +++ b/client/src/app/site/motions/motion-list/motion-list.component.ts @@ -7,6 +7,9 @@ import { Motion } from '../../../shared/models/motions/motion'; import { MatTable, MatPaginator, MatSort, MatTableDataSource } from '@angular/material'; import { Workflow } from '../../../shared/models/motions/workflow'; +/** + * Component that displays all the motions in a Table using DataSource. + */ @Component({ selector: 'app-motion-list', templateUrl: './motion-list.component.html', @@ -28,14 +31,26 @@ export class MotionListComponent extends BaseComponent implements OnInit { */ dataSource: MatTableDataSource; + /** + * The table itself. + */ @ViewChild(MatTable) table: MatTable; + + /** + * Pagination. Might be turned off to all motions at once. + */ @ViewChild(MatPaginator) paginator: MatPaginator; + + /** + * Sort the Table + */ @ViewChild(MatSort) sort: MatSort; /** * Use for minimal width */ columnsToDisplayMinWidth = ['identifier', 'title', 'state']; + /** * Use for maximal width */ @@ -43,13 +58,16 @@ export class MotionListComponent extends BaseComponent implements OnInit { /** * Constructor implements title and translation Module. - * @param titleService - * @param translate + * + * @param titleService Title + * @param translate Translation + * @param router Router + * @param route Current route */ constructor( - public router: Router, - titleService: Title, + protected titleService: Title, protected translate: TranslateService, + private router: Router, private route: ActivatedRoute ) { super(titleService, translate); @@ -78,6 +96,11 @@ export class MotionListComponent extends BaseComponent implements OnInit { }); } + /** + * Select a motion from list. Executed via click. + * + * @param motion The row the user clicked at + */ selectMotion(motion) { this.router.navigate(['./' + motion.id], { relativeTo: this.route }); } @@ -102,6 +125,8 @@ export class MotionListComponent extends BaseComponent implements OnInit { /** * Download all motions As PDF and DocX + * + * TODO: Currently does nothing */ downloadMotionsButton() { console.log('Download Motions Button'); diff --git a/client/src/app/site/start/start.component.ts b/client/src/app/site/start/start.component.ts index 6030d5b84..cc4db77d3 100644 --- a/client/src/app/site/start/start.component.ts +++ b/client/src/app/site/start/start.component.ts @@ -111,8 +111,7 @@ export class StartComponent extends BaseComponent implements OnInit { * function to print datastore */ giveDataStore() { - // this.DS.printWhole(); - console.log('only the motions: \n', this.DS.get(Motion) as Motion[]); + this.DS.printWhole(); } /** From 70416df50b54eea4b4972c12c67f53e6b4df8004 Mon Sep 17 00:00:00 2001 From: Sean Engelhardt Date: Wed, 22 Aug 2018 16:03:49 +0200 Subject: [PATCH 6/6] Add data-send, option to delete motion --- client/src/app/core/core.module.ts | 4 +- .../app/core/services/autoupdate.service.ts | 8 +- .../core/services/data-send.service.spec.ts | 15 ++++ .../app/core/services/data-send.service.ts | 74 +++++++++++++++++++ ...ice.spec.ts => data-store.service.spec.ts} | 2 +- ...Store.service.ts => data-store.service.ts} | 71 ++++-------------- client/src/app/openslides.component.ts | 2 +- .../motion-detail.component.html | 16 +++- .../motion-detail.component.scss | 4 + .../motion-detail/motion-detail.component.ts | 44 +++++------ .../motion-list/motion-list.component.ts | 6 +- client/src/app/site/site.component.ts | 8 +- 12 files changed, 157 insertions(+), 97 deletions(-) create mode 100644 client/src/app/core/services/data-send.service.spec.ts create mode 100644 client/src/app/core/services/data-send.service.ts rename client/src/app/core/services/{dataStore.service.spec.ts => data-store.service.spec.ts} (78%) rename client/src/app/core/services/{dataStore.service.ts => data-store.service.ts} (73%) diff --git a/client/src/app/core/core.module.ts b/client/src/app/core/core.module.ts index 48e5c9661..28352a857 100644 --- a/client/src/app/core/core.module.ts +++ b/client/src/app/core/core.module.ts @@ -7,10 +7,11 @@ import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { AuthGuard } from './services/auth-guard.service'; import { AuthService } from './services/auth.service'; import { AutoupdateService } from './services/autoupdate.service'; -import { DataStoreService } from './services/dataStore.service'; +import { DataStoreService } from './services/data-store.service'; import { OperatorService } from './services/operator.service'; import { WebsocketService } from './services/websocket.service'; import { AddHeaderInterceptor } from './http-interceptor'; +import { DataSendService } from './services/data-send.service'; /** Global Core Module. Contains all global (singleton) services * @@ -23,6 +24,7 @@ import { AddHeaderInterceptor } from './http-interceptor'; AuthService, AutoupdateService, DataStoreService, + DataSendService, OperatorService, WebsocketService, { diff --git a/client/src/app/core/services/autoupdate.service.ts b/client/src/app/core/services/autoupdate.service.ts index 60d5bf363..c60c9fb5b 100644 --- a/client/src/app/core/services/autoupdate.service.ts +++ b/client/src/app/core/services/autoupdate.service.ts @@ -68,7 +68,13 @@ export class AutoupdateService extends OpenSlidesComponent { storeResponse(socketResponse): void { socketResponse.forEach(jsonObj => { const targetClass = this.getClassFromCollectionString(jsonObj.collection); - this.DS.add(new targetClass().deserialize(jsonObj.data)); + if (jsonObj.action === 'deleted') { + console.log('storeResponse detect delete'); + + this.DS.remove(jsonObj.collection, jsonObj.id); + } else { + this.DS.add(new targetClass().deserialize(jsonObj.data)); + } }); } diff --git a/client/src/app/core/services/data-send.service.spec.ts b/client/src/app/core/services/data-send.service.spec.ts new file mode 100644 index 000000000..4d66e43f8 --- /dev/null +++ b/client/src/app/core/services/data-send.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { DataSendService } from './data-send.service'; + +describe('DataSendService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [DataSendService] + }); + }); + + it('should be created', inject([DataSendService], (service: DataSendService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/client/src/app/core/services/data-send.service.ts b/client/src/app/core/services/data-send.service.ts new file mode 100644 index 000000000..4fbaad5c2 --- /dev/null +++ b/client/src/app/core/services/data-send.service.ts @@ -0,0 +1,74 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { BaseModel } from '../../shared/models/base.model'; +import { Observable } from 'rxjs'; +import { tap } from 'rxjs/operators'; + +/** + * Send data back to server + * + * Contrast to dataStore service + */ +@Injectable({ + providedIn: 'root' +}) +export class DataSendService { + /** + * Construct a DataSendService + * + * @param http The HTTP Client + */ + constructor(private http: HttpClient) {} + + /** + * Save motion in the server + * + * @return Observable from + */ + saveModel(model: BaseModel): Observable { + if (!model.id) { + return this.http.post('rest/' + model.collectionString + '/', model).pipe( + tap( + response => { + // TODO: Message, Notify, Etc + console.log('New Model added. Response : ', response); + }, + error => console.log('error. ', error) + ) + ); + } else { + return this.http.put('rest/' + model.collectionString + '/' + model.id, model).pipe( + tap( + response => { + console.log('Update model. Response : ', response); + }, + error => console.log('error. ', error) + ) + ); + } + } + + /** + * Deletes the given model on the server + * + * @param model the BaseModel that shall be removed + * @return Observable of BaseModel + * + * TODO Not tested + */ + delete(model: BaseModel): Observable { + if (model.id) { + return this.http.delete('rest/' + model.collectionString + '/' + model.id).pipe( + tap( + response => { + // TODO: Message, Notify, Etc + console.log('the response: ', response); + }, + error => console.error('error during delete: ', error) + ) + ); + } else { + console.error('No model ID to delete'); + } + } +} diff --git a/client/src/app/core/services/dataStore.service.spec.ts b/client/src/app/core/services/data-store.service.spec.ts similarity index 78% rename from client/src/app/core/services/dataStore.service.spec.ts rename to client/src/app/core/services/data-store.service.spec.ts index 70f76d8fe..d5269ab50 100644 --- a/client/src/app/core/services/dataStore.service.spec.ts +++ b/client/src/app/core/services/data-store.service.spec.ts @@ -1,6 +1,6 @@ import { TestBed, inject } from '@angular/core/testing'; -import { DataStoreService } from './dataStore.service'; +import { DataStoreService } from './data-store.service'; describe('DS', () => { beforeEach(() => { diff --git a/client/src/app/core/services/dataStore.service.ts b/client/src/app/core/services/data-store.service.ts similarity index 73% rename from client/src/app/core/services/dataStore.service.ts rename to client/src/app/core/services/data-store.service.ts index 29af1e678..fb98490c8 100644 --- a/client/src/app/core/services/dataStore.service.ts +++ b/client/src/app/core/services/data-store.service.ts @@ -1,7 +1,5 @@ import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; import { Observable, BehaviorSubject } from 'rxjs'; -import { tap } from 'rxjs/operators'; import { ImproperlyConfiguredError } from 'app/core/exceptions'; import { BaseModel, ModelId } from 'app/shared/models/base.model'; @@ -55,9 +53,7 @@ export class DataStoreService { * Empty constructor for dataStore * @param http use HttpClient to send models back to the server */ - constructor(private http: HttpClient) { - console.log('constructor of dataStore. http: ', this.http); - } + constructor() {} /** * Read one, multiple or all ID's from dataStore @@ -153,62 +149,25 @@ export class DataStoreService { * @param ...ids An or multiple IDs or a list of IDs of BaseModels. use spread operator ("...") for arrays * @example this.DS.remove(User, myUser.id, 3, 4) */ - remove(Type, ...ids: ModelId[]): void { + remove(collectionType, ...ids: ModelId[]): void { + console.log('remove from DS: collection', collectionType); + console.log('remove from DS: collection', ids); + + let collectionString: string; + if (typeof collectionType === 'string') { + collectionString = collectionType; + } else { + const tempObject = new collectionType(); + collectionString = tempObject.collectionString; + } + ids.forEach(id => { - const tempObject = new Type(); - if (DataStoreService.store[tempObject.collectionString]) { - delete DataStoreService.store[tempObject.collectionString][id]; - console.log(`did remove "${id}" from Datastore "${tempObject.collectionString}"`); + if (DataStoreService.store[collectionString]) { + delete DataStoreService.store[collectionString][id]; } }); } - /** - * Saves the given model on the server - * @param model the BaseModel that shall be saved - * @return Observable of BaseModel - */ - save(model: BaseModel): Observable { - if (!model.id) { - return this.http.post('rest/' + model.collectionString + '/', model).pipe( - tap( - response => { - console.log('New Model added. Response : ', response); - }, - error => console.log('error. ', error) - ) - ); - } else { - return this.http.put('rest/' + model.collectionString + '/' + model.id, model).pipe( - tap( - response => { - console.log('Update model. Response : ', response); - }, - error => console.log('error. ', error) - ) - ); - } - } - - /** - * Deletes the given model on the server - * @param model the BaseModel that shall be removed - * @return Observable of BaseModel - */ - delete(model: BaseModel): Observable { - if (!model.id) { - throw new ImproperlyConfiguredError('The model must have an id!'); - } - - // TODO not tested - return this.http.post(model.collectionString + '/', model).pipe( - tap(response => { - console.log('the response: ', response); - this.remove(model, model.id); - }) - ); - } - /** * Observe the dataStore for changes. * @return an observable behaviorSubject diff --git a/client/src/app/openslides.component.ts b/client/src/app/openslides.component.ts index 570684bfb..c78939e0e 100644 --- a/client/src/app/openslides.component.ts +++ b/client/src/app/openslides.component.ts @@ -2,7 +2,7 @@ import { Injector } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable, of } from 'rxjs'; -import { DataStoreService } from 'app/core/services/dataStore.service'; +import { DataStoreService } from './core/services/data-store.service'; /** * injects the {@link DataStoreService} to all its children and provides a generic function to catch errors diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.html b/client/src/app/site/motions/motion-detail/motion-detail.component.html index 6a4d9bc26..0a8e475d1 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.html +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.html @@ -19,6 +19,18 @@ by {{motion.submitterAsUser}}
+ + + + + + + + + + @@ -85,7 +97,7 @@

{{motion.recomBy}}

{{motion.recommendation.name}}
- + {{state}} @@ -103,7 +115,7 @@

Category

{{motion.category}}
- + None diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.scss b/client/src/app/site/motions/motion-detail/motion-detail.component.scss index 181e831b8..4043eaee2 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.scss +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.scss @@ -6,6 +6,10 @@ span { background-color: rgb(77, 243, 86); } +.deleteMotionButton { + color: red; +} + .motion-title { padding-left: 20px; line-height: 100%; diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.ts b/client/src/app/site/motions/motion-detail/motion-detail.component.ts index df8b8bf89..cf9e99ab0 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.ts +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.ts @@ -5,8 +5,7 @@ import { Motion } from '../../../shared/models/motions/motion'; import { Category } from '../../../shared/models/motions/category'; import { FormGroup, FormBuilder } from '@angular/forms'; import { MatExpansionPanel } from '@angular/material'; -import { DataStoreService } from '../../../core/services/dataStore.service'; -import { OperatorService } from '../../../core/services/operator.service'; +import { DataSendService } from '../../../core/services/data-send.service'; /** * Component for the motion detail view @@ -16,8 +15,7 @@ import { OperatorService } from '../../../core/services/operator.service'; templateUrl: './motion-detail.component.html', styleUrls: ['./motion-detail.component.scss'] }) -// export class MotionDetailComponent extends BaseComponent implements OnInit { -export class MotionDetailComponent implements OnInit { +export class MotionDetailComponent extends BaseComponent implements OnInit { /** * MatExpansionPanel for the meta info */ @@ -56,8 +54,6 @@ export class MotionDetailComponent implements OnInit { /** * Constuct the detail view. * - * TODO: DataStore needs removed and added via the parent. - * Own service for put and post required * * @param route determine if this is a new or an existing motion * @param formBuilder For reactive forms. Form Group and Form Control @@ -66,11 +62,9 @@ export class MotionDetailComponent implements OnInit { private router: Router, private route: ActivatedRoute, private formBuilder: FormBuilder, - private operator: OperatorService, - private myDataStore: DataStoreService + private dataSend: DataSendService ) { - // TODO: Add super again - // super(); + super(); this.createForm(); if (route.snapshot.url[0].path === 'new') { @@ -80,13 +74,11 @@ export class MotionDetailComponent implements OnInit { } else { // load existing motion this.route.params.subscribe(params => { - console.log('params ', params); - // has the motion of the DataStore was initialized before. - this.motion = this.myDataStore.get(Motion, params.id) as Motion; + this.motion = this.DS.get(Motion, params.id) as Motion; // Observe motion to get the motion in the parameter and also get the changes - this.myDataStore.getObservable().subscribe(newModel => { + this.DS.getObservable().subscribe(newModel => { if (newModel instanceof Motion) { if (newModel.id === +params.id) { this.motion = newModel as Motion; @@ -150,8 +142,7 @@ export class MotionDetailComponent implements OnInit { this.motion.title = this.motion.currentTitle; this.motion.text = this.motion.currentText; - this.myDataStore.save(this.motion).subscribe(answer => { - console.log('answer, ', answer); + this.dataSend.saveModel(this.motion).subscribe(answer => { if (answer && answer.id && this.newMotion) { this.router.navigate(['./motions/' + answer.id]); } @@ -162,7 +153,7 @@ export class MotionDetailComponent implements OnInit { * return all Categories. */ getMotionCategories(): Category[] { - const categories = this.myDataStore.get(Category); + const categories = this.DS.get(Category); return categories as Category[]; } @@ -171,7 +162,6 @@ export class MotionDetailComponent implements OnInit { */ editMotionButton() { this.editMotion ? (this.editMotion = false) : (this.editMotion = true); - if (this.editMotion) { this.patchForm(); this.metaInfoPanel.open(); @@ -181,17 +171,17 @@ export class MotionDetailComponent implements OnInit { } } + /** + * Trigger to delete the motion + */ + deleteMotionButton() { + this.dataSend.delete(this.motion).subscribe(answer => { + this.router.navigate(['./motions/']); + }); + } + /** * Init. Does nothing here. */ ngOnInit() {} - - /** - * Function to download a motion. - * - * TODO: does nothing yet. - */ - downloadSingleMotionButton() { - console.log('Download this motion'); - } } diff --git a/client/src/app/site/motions/motion-list/motion-list.component.ts b/client/src/app/site/motions/motion-list/motion-list.component.ts index 820a25602..456ea6356 100644 --- a/client/src/app/site/motions/motion-list/motion-list.component.ts +++ b/client/src/app/site/motions/motion-list/motion-list.component.ts @@ -88,10 +88,8 @@ export class MotionListComponent extends BaseComponent implements OnInit { // The alternative approach is to put the observable as DataSource to the table this.DS.getObservable().subscribe(newModel => { if (newModel instanceof Motion) { - if (!this.motionArray.includes(newModel)) { - this.motionArray.push(newModel as Motion); - this.dataSource.data = this.motionArray; - } + this.motionArray = this.DS.get(Motion) as Motion[]; + this.dataSource.data = this.motionArray; } }); } diff --git a/client/src/app/site/site.component.ts b/client/src/app/site/site.component.ts index c1880146f..f9fd31883 100644 --- a/client/src/app/site/site.component.ts +++ b/client/src/app/site/site.component.ts @@ -65,12 +65,12 @@ export class SiteComponent extends BaseComponent implements OnInit { } }); - //get a translation via code: use the translation service + // get a translation via code: use the translation service // this.translate.get('Motions').subscribe((res: string) => { - // console.log('translation of motions in the target language: ' + res); - // }); + // console.log('translation of motions in the target language: ' + res); + // }); - //start autoupdate if the user is logged in: + // start autoupdate if the user is logged in: this.operator.whoAmI().subscribe(resp => { if (resp.user) { this.autoupdateService.startAutoupdate();