Merge pull request #3905 from FinnStutzenstein/commentfields
Motion comment section list
This commit is contained in:
commit
24abdc7bd0
@ -13,6 +13,7 @@ import { WebsocketService } from './services/websocket.service';
|
||||
import { AddHeaderInterceptor } from './http-interceptor';
|
||||
import { DataSendService } from './services/data-send.service';
|
||||
import { ViewportService } from './services/viewport.service';
|
||||
import { PromptDialogComponent } from '../shared/components/prompt-dialog/prompt-dialog.component';
|
||||
|
||||
/** Global Core Module. Contains all global (singleton) services
|
||||
*
|
||||
@ -34,7 +35,8 @@ import { ViewportService } from './services/viewport.service';
|
||||
useClass: AddHeaderInterceptor,
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
],
|
||||
entryComponents: [PromptDialogComponent]
|
||||
})
|
||||
export class CoreModule {
|
||||
/** make sure CoreModule is imported only by one NgModule, the AppModule */
|
||||
|
17
client/src/app/core/services/prompt.service.spec.ts
Normal file
17
client/src/app/core/services/prompt.service.spec.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { TestBed, inject } from '@angular/core/testing';
|
||||
|
||||
import { PromptService } from './prompt.service';
|
||||
import { E2EImportsModule } from 'e2e-imports.module';
|
||||
|
||||
describe('PromptService', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [E2EImportsModule],
|
||||
providers: [PromptService]
|
||||
});
|
||||
});
|
||||
|
||||
it('should be created', inject([PromptService], (service: PromptService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
});
|
30
client/src/app/core/services/prompt.service.ts
Normal file
30
client/src/app/core/services/prompt.service.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { OpenSlidesComponent } from 'app/openslides.component';
|
||||
import { PromptDialogComponent } from '../../shared/components/prompt-dialog/prompt-dialog.component';
|
||||
import { MatDialog } from '@angular/material';
|
||||
|
||||
/**
|
||||
* A general service for prompting 'yes' or 'cancel' thinks from the user.
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class PromptService extends OpenSlidesComponent {
|
||||
public constructor(private dialog: MatDialog) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the dialog. Returns true, if the user accepts.
|
||||
* @param title The title to display in the dialog
|
||||
* @param content The content in the dialog
|
||||
*/
|
||||
public async open(title: string, content: string): Promise<any> {
|
||||
const dialogRef = this.dialog.open(PromptDialogComponent, {
|
||||
width: '250px',
|
||||
data: { title: title, content: content }
|
||||
});
|
||||
|
||||
return dialogRef.afterClosed().toPromise();
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
<h2 mat-dialog-title>{{ data.title | translate }}</h2>
|
||||
<mat-dialog-content>{{ data.content | translate }}</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<button mat-button [mat-dialog-close]="true" color="warn" translate>Yes</button>
|
||||
<button mat-button [mat-dialog-close]="false" translate>Cancel</button>
|
||||
</mat-dialog-actions>
|
@ -0,0 +1,26 @@
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
|
||||
// import { PromptDialogComponent } from './prompt-dialog.component';
|
||||
import { E2EImportsModule } from 'e2e-imports.module';
|
||||
|
||||
describe('PromptDialogComponent', () => {
|
||||
// let component: PromptDialogComponent;
|
||||
// let fixture: ComponentFixture<PromptDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [E2EImportsModule]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
// TODO: You cannot create this component in the standard way. Needs different testing.
|
||||
beforeEach(() => {
|
||||
/*fixture = TestBed.createComponent(PromptDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();*/
|
||||
});
|
||||
|
||||
/*it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});*/
|
||||
});
|
@ -0,0 +1,21 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
|
||||
|
||||
interface PromptDialogData {
|
||||
title: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple prompt dialog. Takes a title and content.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'os-prompt-dialog',
|
||||
templateUrl: './prompt-dialog.component.html'
|
||||
})
|
||||
export class PromptDialogComponent {
|
||||
public constructor(
|
||||
public dialogRef: MatDialogRef<PromptDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: PromptDialogData
|
||||
) {}
|
||||
}
|
@ -50,6 +50,7 @@ import { LegalNoticeContentComponent } from './components/legal-notice-content/l
|
||||
import { PrivacyPolicyContentComponent } from './components/privacy-policy-content/privacy-policy-content.component';
|
||||
import { SearchValueSelectorComponent } from './components/search-value-selector/search-value-selector.component';
|
||||
import { OpenSlidesDateAdapter } from './date-adapter';
|
||||
import { PromptDialogComponent } from './components/prompt-dialog/prompt-dialog.component';
|
||||
|
||||
library.add(fas);
|
||||
|
||||
@ -127,7 +128,8 @@ library.add(fas);
|
||||
HeadBarComponent,
|
||||
SearchValueSelectorComponent,
|
||||
LegalNoticeContentComponent,
|
||||
PrivacyPolicyContentComponent
|
||||
PrivacyPolicyContentComponent,
|
||||
PromptDialogComponent
|
||||
],
|
||||
declarations: [
|
||||
PermsDirective,
|
||||
@ -136,7 +138,8 @@ library.add(fas);
|
||||
FooterComponent,
|
||||
LegalNoticeContentComponent,
|
||||
PrivacyPolicyContentComponent,
|
||||
SearchValueSelectorComponent
|
||||
SearchValueSelectorComponent,
|
||||
PromptDialogComponent
|
||||
],
|
||||
providers: [{ provide: DateAdapter, useClass: OpenSlidesDateAdapter }]
|
||||
})
|
||||
|
@ -0,0 +1,108 @@
|
||||
<os-head-bar appName="Motion comment sections" [plusButton]=true (plusButtonClicked)=onPlusButton()>
|
||||
</os-head-bar>
|
||||
<div class="head-spacer"></div>
|
||||
<mat-card *ngIf="commentSectionToCreate">
|
||||
<mat-card-title translate>Create new comment section</mat-card-title>
|
||||
<mat-card-content>
|
||||
<form [formGroup]="createForm" (keydown)="keyDownFunction($event)">
|
||||
<p>
|
||||
<mat-form-field>
|
||||
<input formControlName="name" matInput placeholder="{{'Name' | translate}}" required>
|
||||
<mat-hint *ngIf="!createForm.controls.name.valid">
|
||||
<span translate>Required</span>
|
||||
</mat-hint>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
<p>
|
||||
<os-search-value-selector ngDefaultControl [form]="createForm" [formControl]="this.createForm.get('read_groups_id')"
|
||||
[multiple]="true" listname="Groups with read permissions" [InputListValues]="this.groups"></os-search-value-selector>
|
||||
</p>
|
||||
<p>
|
||||
<os-search-value-selector ngDefaultControl [form]="createForm" [formControl]="this.createForm.get('write_groups_id')"
|
||||
[multiple]="true" listname="Groups with write permissions" [InputListValues]="this.groups"></os-search-value-selector>
|
||||
</p>
|
||||
</form>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button mat-button (click)="create()" translate>Create</button>
|
||||
<button mat-button (click)="commentSectionToCreate = null" translate>Abort</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
<mat-accordion class="os-card">
|
||||
<mat-expansion-panel *ngFor="let section of this.commentSections" (opened)="openId = section.id"
|
||||
(closed)="panelClosed(section)" [expanded]="openId === section.id" multiple="false">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
<div class="header-container">
|
||||
<div class="name">
|
||||
{{ section.name }}
|
||||
</div>
|
||||
<div class="read">
|
||||
<fa-icon icon="eye"></fa-icon>
|
||||
{{ section.read_groups }}
|
||||
<ng-container *ngIf="section.read_groups.length === 0">
|
||||
–
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="write">
|
||||
<fa-icon icon="pen"></fa-icon>
|
||||
{{ section.write_groups }}
|
||||
<ng-container *ngIf="section.write_groups.length === 0">
|
||||
–
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<form [formGroup]="updateForm" *ngIf="editId === section.id" (keydown)="keyDownFunction($event, section)">
|
||||
<span translate>Edit section details:</span>
|
||||
<p>
|
||||
<mat-form-field>
|
||||
<input formControlName="name" matInput placeholder="{{'Name' | translate}}">
|
||||
<mat-hint *ngIf="!updateForm.controls.name.valid">
|
||||
<span translate>Required</span>
|
||||
</mat-hint>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
<p>
|
||||
<os-search-value-selector ngDefaultControl [form]="updateForm" [formControl]="this.updateForm.get('read_groups_id')"
|
||||
[multiple]="true" listname="Groups with read permissions" [InputListValues]="this.groups"></os-search-value-selector>
|
||||
</p>
|
||||
<p>
|
||||
<os-search-value-selector ngDefaultControl [form]="updateForm" [formControl]="this.updateForm.get('write_groups_id')"
|
||||
[multiple]="true" listname="Groups with write permissions" [InputListValues]="this.groups"></os-search-value-selector>
|
||||
</p>
|
||||
</form>
|
||||
<ng-container *ngIf="editId !== section.id">
|
||||
<h3 translate>Name</h3>
|
||||
<div class="spacer-left">{{ section.name }}</div>
|
||||
<h3 translate>Groups with read permissions</h3>
|
||||
<ul *ngFor="let group of section.read_groups">
|
||||
<li>{{ group.getTitle() }}</li>
|
||||
</ul>
|
||||
<div class="spacer-left" *ngIf="section.read_groups.length === 0" translate>No groups selected</div>
|
||||
<h3 translate>Groups with write permissions</h3>
|
||||
<ul *ngFor="let group of section.write_groups">
|
||||
<li>{{ group.getTitle() }}</li>
|
||||
</ul>
|
||||
<div class="spacer-left" *ngIf="section.write_groups.length === 0" translate>No groups selected</div>
|
||||
</ng-container>
|
||||
<mat-action-row>
|
||||
<button *ngIf="editId !== section.id" mat-button class="on-transition-fade" (click)="onEditButton(section)"
|
||||
mat-icon-button>
|
||||
<fa-icon icon='pen'></fa-icon>
|
||||
</button>
|
||||
<button *ngIf="editId === section.id" mat-button class="on-transition-fade" (click)="editId = null"
|
||||
mat-icon-button>
|
||||
<fa-icon icon='times'></fa-icon>
|
||||
</button>
|
||||
<button *ngIf="editId === section.id" mat-button class="on-transition-fade" (click)="onSaveButton(section)"
|
||||
mat-icon-button>
|
||||
<fa-icon icon='save'></fa-icon>
|
||||
</button>
|
||||
<button mat-button class='on-transition-fade' (click)=onDeleteButton(section) mat-icon-button>
|
||||
<fa-icon icon='trash'></fa-icon>
|
||||
</button>
|
||||
</mat-action-row>
|
||||
</mat-expansion-panel>
|
||||
</mat-accordion>
|
@ -0,0 +1,49 @@
|
||||
.head-spacer {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
text-align: right;
|
||||
background: white;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
mat-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
display: grid;
|
||||
grid-template-rows: auto;
|
||||
grid-template-columns: 33.333% 33.333% 33.333%;
|
||||
width: 100%;
|
||||
|
||||
> div {
|
||||
grid-row-start: 1;
|
||||
grid-row-end: span 1;
|
||||
grid-column-end: span 1;
|
||||
}
|
||||
|
||||
.title {
|
||||
grid-column-start: 1;
|
||||
}
|
||||
|
||||
.read {
|
||||
grid-column-start: 2;
|
||||
}
|
||||
|
||||
.write {
|
||||
grid-column-start: 3;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
display: block;
|
||||
margin-top: 12px; //distance between heading and text
|
||||
margin-bottom: 3px; //distance between heading and text
|
||||
font-size: 90%;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.spacer-left {
|
||||
margin-left: 40px;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MotionCommentSectionListComponent } from './motion-comment-section-list.component';
|
||||
import { E2EImportsModule } from 'e2e-imports.module';
|
||||
|
||||
describe('MotionCommentSectionListComponent', () => {
|
||||
let component: MotionCommentSectionListComponent;
|
||||
let fixture: ComponentFixture<MotionCommentSectionListComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [E2EImportsModule],
|
||||
declarations: [MotionCommentSectionListComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MotionCommentSectionListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,170 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { BaseComponent } from '../../../../base.component';
|
||||
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||
import { MotionCommentSection } from '../../../../shared/models/motions/motion-comment-section';
|
||||
import { ViewMotionCommentSection } from '../../models/view-motion-comment-section';
|
||||
import { MotionCommentSectionRepositoryService } from '../../services/motion-comment-section-repository.service';
|
||||
import { PromptService } from '../../../../core/services/prompt.service';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { Group } from '../../../../shared/models/users/group';
|
||||
import { DataStoreService } from '../../../../core/services/data-store.service';
|
||||
|
||||
/**
|
||||
* List view for the categories.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'os-motion-comment-section-list',
|
||||
templateUrl: './motion-comment-section-list.component.html',
|
||||
styleUrls: ['./motion-comment-section-list.component.scss']
|
||||
})
|
||||
export class MotionCommentSectionListComponent extends BaseComponent implements OnInit {
|
||||
public commentSectionToCreate: MotionCommentSection | null;
|
||||
|
||||
/**
|
||||
* Source of the Data
|
||||
*/
|
||||
public commentSections: ViewMotionCommentSection[] = [];
|
||||
|
||||
/**
|
||||
* The current focussed formgroup
|
||||
*/
|
||||
public updateForm: FormGroup;
|
||||
|
||||
public createForm: FormGroup;
|
||||
|
||||
public openId: number | null;
|
||||
public editId: number | null;
|
||||
|
||||
public groups: BehaviorSubject<Array<Group>>;
|
||||
|
||||
/**
|
||||
* The usual component constructor
|
||||
* @param titleService
|
||||
* @param translate
|
||||
* @param repo
|
||||
* @param formBuilder
|
||||
*/
|
||||
public constructor(
|
||||
protected titleService: Title,
|
||||
protected translate: TranslateService,
|
||||
private repo: MotionCommentSectionRepositoryService,
|
||||
private formBuilder: FormBuilder,
|
||||
private promptService: PromptService,
|
||||
private DS: DataStoreService
|
||||
) {
|
||||
super(titleService, translate);
|
||||
const form = {
|
||||
name: ['', Validators.required],
|
||||
read_groups_id: [[]],
|
||||
write_groups_id: [[]]
|
||||
};
|
||||
this.createForm = this.formBuilder.group(form);
|
||||
this.updateForm = this.formBuilder.group(form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Event on Key Down in update or create form. Do not provide the viewSection for the create form.
|
||||
*/
|
||||
public keyDownFunction(event: KeyboardEvent, viewSection?: ViewMotionCommentSection): void {
|
||||
if (event.keyCode === 13) {
|
||||
if (viewSection) {
|
||||
this.onSaveButton(viewSection);
|
||||
} else {
|
||||
this.create();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init function.
|
||||
*
|
||||
* Sets the title and gets/observes categories from DataStore
|
||||
*/
|
||||
public ngOnInit(): void {
|
||||
super.setTitle('Comment Sections');
|
||||
this.groups = new BehaviorSubject(this.DS.getAll(Group));
|
||||
this.DS.changeObservable.subscribe(model => {
|
||||
if (model instanceof Group) {
|
||||
this.groups.next(this.DS.getAll(Group));
|
||||
}
|
||||
});
|
||||
this.repo.getViewModelListObservable().subscribe(newViewSections => {
|
||||
this.commentSections = newViewSections;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new Section.
|
||||
*/
|
||||
public onPlusButton(): void {
|
||||
if (!this.commentSectionToCreate) {
|
||||
this.commentSectionToCreate = new MotionCommentSection();
|
||||
this.createForm.setValue({
|
||||
name: '',
|
||||
read_groups_id: [],
|
||||
write_groups_id: []
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public create(): void {
|
||||
if (this.createForm.valid) {
|
||||
this.commentSectionToCreate.patchValues(this.createForm.value as MotionCommentSection);
|
||||
this.repo.create(this.commentSectionToCreate).subscribe(resp => {
|
||||
this.commentSectionToCreate = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executed on edit button
|
||||
* @param viewSection
|
||||
*/
|
||||
public onEditButton(viewSection: ViewMotionCommentSection): void {
|
||||
this.editId = viewSection.id;
|
||||
|
||||
this.updateForm.setValue({
|
||||
name: viewSection.name,
|
||||
read_groups_id: viewSection.read_groups_id,
|
||||
write_groups_id: viewSection.write_groups_id
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the categories
|
||||
*/
|
||||
public onSaveButton(viewSection: ViewMotionCommentSection): void {
|
||||
if (this.updateForm.valid) {
|
||||
this.repo.update(this.updateForm.value as Partial<MotionCommentSection>, viewSection).subscribe(resp => {
|
||||
this.openId = this.editId = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* is executed, when the delete button is pressed
|
||||
*/
|
||||
public async onDeleteButton(viewSection: ViewMotionCommentSection): Promise<any> {
|
||||
const content = this.translate.instant('Delete') + ` ${viewSection.name}?`;
|
||||
if (await this.promptService.open('Are you sure?', content)) {
|
||||
this.repo.delete(viewSection).subscribe(resp => {
|
||||
this.openId = this.editId = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is executed when a mat-extension-panel is closed
|
||||
* @param viewSection the category in the panel
|
||||
*/
|
||||
public panelClosed(viewSection: ViewMotionCommentSection): void {
|
||||
this.openId = null;
|
||||
if (this.editId) {
|
||||
this.onSaveButton(viewSection);
|
||||
}
|
||||
}
|
||||
}
|
@ -191,7 +191,7 @@
|
||||
<mat-option *ngFor="let state of motionCopy.nextStates" [value]="state.id">{{state}}</mat-option>
|
||||
<mat-divider></mat-divider>
|
||||
<mat-option>
|
||||
<fa-icon icon='exclamation-triangle'></fa-icon> <span translate>Reset State</span>
|
||||
<fa-icon icon='exclamation-triangle'></fa-icon><span translate>Reset State</span>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
@ -42,6 +42,10 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
||||
{
|
||||
text: 'Categories',
|
||||
action: 'toCategories'
|
||||
},
|
||||
{
|
||||
text: 'Motion comment sections',
|
||||
action: 'toMotionCommentSections'
|
||||
}
|
||||
];
|
||||
|
||||
@ -130,6 +134,13 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
||||
this.router.navigate(['./category'], { relativeTo: this.route });
|
||||
}
|
||||
|
||||
/**
|
||||
* navigate to 'motion/comment-section'
|
||||
*/
|
||||
public toMotionCommentSections(): void {
|
||||
this.router.navigate(['./comment-section'], { relativeTo: this.route });
|
||||
}
|
||||
|
||||
/**
|
||||
* Download all motions As PDF and DocX
|
||||
*
|
||||
|
@ -0,0 +1,90 @@
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { BaseViewModel } from '../../base/base-view-model';
|
||||
import { MotionCommentSection } from '../../../shared/models/motions/motion-comment-section';
|
||||
import { Group } from '../../../shared/models/users/group';
|
||||
import { BaseModel } from '../../../shared/models/base/base-model';
|
||||
|
||||
/**
|
||||
* Motion comment section class for the View
|
||||
*
|
||||
* Stores a motion comment section including all (implicit) references
|
||||
* Provides "safe" access to variables and functions in {@link MotionCommentSection}
|
||||
* @ignore
|
||||
*/
|
||||
export class ViewMotionCommentSection extends BaseViewModel {
|
||||
private _section: MotionCommentSection;
|
||||
|
||||
private _read_groups: Group[];
|
||||
private _write_groups: Group[];
|
||||
|
||||
public edit = false;
|
||||
public open = false;
|
||||
|
||||
public get section(): MotionCommentSection {
|
||||
return this._section;
|
||||
}
|
||||
|
||||
public get id(): number {
|
||||
return this.section ? this.section.id : null;
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
return this.section ? this.section.name : null;
|
||||
}
|
||||
|
||||
public get read_groups_id(): number[] {
|
||||
return this.section ? this.section.read_groups_id : [];
|
||||
}
|
||||
|
||||
public get write_groups_id(): number[] {
|
||||
return this.section ? this.section.write_groups_id : [];
|
||||
}
|
||||
|
||||
public get read_groups(): Group[] {
|
||||
return this._read_groups;
|
||||
}
|
||||
|
||||
public get write_groups(): Group[] {
|
||||
return this._write_groups;
|
||||
}
|
||||
|
||||
public set name(name: string) {
|
||||
this._section.name = name;
|
||||
}
|
||||
|
||||
public constructor(section: MotionCommentSection, read_groups: Group[], write_groups: Group[]) {
|
||||
super();
|
||||
this._section = section;
|
||||
this._read_groups = read_groups;
|
||||
this._write_groups = write_groups;
|
||||
}
|
||||
|
||||
public getTitle(translate?: TranslateService): string {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the local objects if required
|
||||
* @param section
|
||||
*/
|
||||
public updateValues(update: BaseModel): void {
|
||||
if (update instanceof MotionCommentSection) {
|
||||
this._section = update as MotionCommentSection;
|
||||
}
|
||||
if (update instanceof Group) {
|
||||
this.updateGroup(update as Group);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement updating of groups
|
||||
public updateGroup(group: Group): void {
|
||||
console.log(this._section, group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate this motion into a copy of itself
|
||||
*/
|
||||
public copy(): ViewMotionCommentSection {
|
||||
return new ViewMotionCommentSection(this._section, this._read_groups, this._write_groups);
|
||||
}
|
||||
}
|
@ -3,10 +3,12 @@ import { Routes, RouterModule } from '@angular/router';
|
||||
import { MotionListComponent } from './components/motion-list/motion-list.component';
|
||||
import { MotionDetailComponent } from './components/motion-detail/motion-detail.component';
|
||||
import { CategoryListComponent } from './components/category-list/category-list.component';
|
||||
import { MotionCommentSectionListComponent } from './components/motion-comment-section-list/motion-comment-section-list.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', component: MotionListComponent },
|
||||
{ path: 'category', component: CategoryListComponent },
|
||||
{ path: 'comment-section', component: MotionCommentSectionListComponent },
|
||||
{ path: 'new', component: MotionDetailComponent },
|
||||
{ path: ':id', component: MotionDetailComponent }
|
||||
];
|
||||
|
@ -6,9 +6,10 @@ import { SharedModule } from '../../shared/shared.module';
|
||||
import { MotionListComponent } from './components/motion-list/motion-list.component';
|
||||
import { MotionDetailComponent } from './components/motion-detail/motion-detail.component';
|
||||
import { CategoryListComponent } from './components/category-list/category-list.component';
|
||||
import { MotionCommentSectionListComponent } from './components/motion-comment-section-list/motion-comment-section-list.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, MotionsRoutingModule, SharedModule],
|
||||
declarations: [MotionListComponent, MotionDetailComponent, CategoryListComponent]
|
||||
declarations: [MotionListComponent, MotionDetailComponent, CategoryListComponent, MotionCommentSectionListComponent]
|
||||
})
|
||||
export class MotionsModule {}
|
||||
|
@ -0,0 +1,20 @@
|
||||
import { TestBed, inject } from '@angular/core/testing';
|
||||
|
||||
import { MotionCommentSectionRepositoryService } from './motion-comment-section-repository.service';
|
||||
import { E2EImportsModule } from 'e2e-imports.module';
|
||||
|
||||
describe('MotionCommentSectionRepositoryService', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [E2EImportsModule],
|
||||
providers: [MotionCommentSectionRepositoryService]
|
||||
});
|
||||
});
|
||||
|
||||
it('should be created', inject(
|
||||
[MotionCommentSectionRepositoryService],
|
||||
(service: MotionCommentSectionRepositoryService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}
|
||||
));
|
||||
});
|
@ -0,0 +1,61 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
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';
|
||||
import { ViewMotionCommentSection } from '../models/view-motion-comment-section';
|
||||
import { MotionCommentSection } from '../../../shared/models/motions/motion-comment-section';
|
||||
import { Group } from '../../../shared/models/users/group';
|
||||
|
||||
/**
|
||||
* 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 MotionCommentSectionRepositoryService extends BaseRepository<
|
||||
ViewMotionCommentSection,
|
||||
MotionCommentSection
|
||||
> {
|
||||
/**
|
||||
* 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, MotionCommentSection, [Group]);
|
||||
}
|
||||
|
||||
protected createViewModel(section: MotionCommentSection): ViewMotionCommentSection {
|
||||
const read_groups = this.DS.getMany(Group, section.read_groups_id);
|
||||
const write_groups = this.DS.getMany(Group, section.write_groups_id);
|
||||
return new ViewMotionCommentSection(section, read_groups, write_groups);
|
||||
}
|
||||
|
||||
public create(section: MotionCommentSection): Observable<any> {
|
||||
return this.dataSend.createModel(section);
|
||||
}
|
||||
|
||||
public update(section: Partial<MotionCommentSection>, viewSection?: ViewMotionCommentSection): Observable<any> {
|
||||
let updateSection: MotionCommentSection;
|
||||
if (viewSection) {
|
||||
updateSection = viewSection.section;
|
||||
} else {
|
||||
updateSection = new MotionCommentSection();
|
||||
}
|
||||
updateSection.patchValues(section);
|
||||
return this.dataSend.updateModel(updateSection, 'put');
|
||||
}
|
||||
|
||||
public delete(viewSection: ViewMotionCommentSection): Observable<any> {
|
||||
return this.dataSend.delete(viewSection.section);
|
||||
}
|
||||
}
|
@ -23,7 +23,6 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
AppModule,
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
|
||||
HttpClientModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
|
@ -5,6 +5,7 @@ from django.utils.translation import ugettext as _
|
||||
|
||||
from ..poll.serializers import default_votes_validator
|
||||
from ..utils.auth import get_group_model
|
||||
from ..utils.autoupdate import inform_changed_data
|
||||
from ..utils.rest_api import (
|
||||
CharField,
|
||||
DecimalField,
|
||||
@ -314,6 +315,12 @@ class MotionCommentSectionSerializer(ModelSerializer):
|
||||
'read_groups',
|
||||
'write_groups',)
|
||||
|
||||
def create(self, validated_data):
|
||||
""" Call inform_changed_data on creation, so the cache includes the groups. """
|
||||
section = super().create(validated_data)
|
||||
inform_changed_data(section)
|
||||
return section
|
||||
|
||||
|
||||
class MotionCommentSerializer(ModelSerializer):
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user