Merge pull request #3968 from FinnStutzenstein/ErrorHandling
error handling
This commit is contained in:
commit
080b6f52ad
@ -1,5 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum for different HTTPMethods
|
* Enum for different HTTPMethods
|
||||||
@ -18,14 +19,14 @@ export enum HTTPMethod {
|
|||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
|
|
||||||
export class HttpService {
|
export class HttpService {
|
||||||
/**
|
/**
|
||||||
* Construct a HttpService
|
* Construct a HttpService
|
||||||
*
|
*
|
||||||
* @param http The HTTP Client
|
* @param http The HTTP Client
|
||||||
|
* @param translate
|
||||||
*/
|
*/
|
||||||
public constructor(private http: HttpClient) {}
|
public constructor(private http: HttpClient, private translate: TranslateService) {}
|
||||||
|
|
||||||
private async send<T>(url: string, method: HTTPMethod, data?: any): Promise<T> {
|
private async send<T>(url: string, method: HTTPMethod, data?: any): Promise<T> {
|
||||||
if (!url.endsWith('/')) {
|
if (!url.endsWith('/')) {
|
||||||
@ -33,15 +34,66 @@ export class HttpService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
body: data,
|
body: data
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await this.http.request<T>(method, url, options).toPromise();
|
const response = await this.http.request<T>(method, url, options).toPromise();
|
||||||
return response;
|
return response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("error", e);
|
throw this.handleError(e);
|
||||||
throw e;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes an error thrown by the HttpClient. Processes it to return a string that can
|
||||||
|
* be presented to the user.
|
||||||
|
* @param e The error thrown.
|
||||||
|
* @returns The prepared and translated message for the user
|
||||||
|
*/
|
||||||
|
private handleError(e: any): string {
|
||||||
|
let error = this.translate.instant('Error') + ': ';
|
||||||
|
// If the error is no HttpErrorResponse, it's not clear what is wrong.
|
||||||
|
if (!(e instanceof HttpErrorResponse)) {
|
||||||
|
console.error('Unknown error thrown by the http client: ', e);
|
||||||
|
error += this.translate.instant('An unknown error occurred.');
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!e.error) {
|
||||||
|
error += this.translate.instant("The server didn't respond.");
|
||||||
|
} else if (typeof e.error === 'object') {
|
||||||
|
if (e.error.detail) {
|
||||||
|
error += this.processErrorTexts(e.error.detail);
|
||||||
|
} else {
|
||||||
|
error = Object.keys(e.error)
|
||||||
|
.map(key => {
|
||||||
|
const capitalizedKey = key.charAt(0).toUpperCase() + key.slice(1);
|
||||||
|
return this.translate.instant(capitalizedKey) + ': ' + this.processErrorTexts(e.error[key]);
|
||||||
|
})
|
||||||
|
.join(', ');
|
||||||
|
}
|
||||||
|
} else if (e.status === 500) {
|
||||||
|
error += this.translate.instant('A server error occured. Please contact your system administrator.');
|
||||||
|
} else if (e.status > 500) {
|
||||||
|
error += this.translate.instant('The server cound not be reached') + ` (${e.status})`
|
||||||
|
} else {
|
||||||
|
error += e.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Errors from the servers may be string or array of strings. This function joins the strings together,
|
||||||
|
* if an array is send.
|
||||||
|
* @param str a string or a string array to join together.
|
||||||
|
*/
|
||||||
|
private processErrorTexts(str: string | string[]): string {
|
||||||
|
if (str instanceof Array) {
|
||||||
|
return str.join(' ');
|
||||||
|
} else {
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import { ViewItem } from '../models/view-item';
|
|||||||
import { ListViewBaseComponent } from '../../base/list-view-base';
|
import { ListViewBaseComponent } from '../../base/list-view-base';
|
||||||
import { AgendaRepositoryService } from '../services/agenda-repository.service';
|
import { AgendaRepositoryService } from '../services/agenda-repository.service';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
import { MatSnackBar } from '@angular/material';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List view for the agenda.
|
* List view for the agenda.
|
||||||
@ -21,14 +22,18 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem> impleme
|
|||||||
* The usual constructor for components
|
* The usual constructor for components
|
||||||
* @param titleService
|
* @param titleService
|
||||||
* @param translate
|
* @param translate
|
||||||
|
* @param matSnackBar
|
||||||
|
* @param router
|
||||||
|
* @param repo
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
titleService: Title,
|
titleService: Title,
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private repo: AgendaRepositoryService
|
private repo: AgendaRepositoryService
|
||||||
) {
|
) {
|
||||||
super(titleService, translate);
|
super(titleService, translate, matSnackBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4,6 +4,7 @@ import { Title } from '@angular/platform-browser';
|
|||||||
import { ViewAssignment } from '../models/view-assignment';
|
import { ViewAssignment } from '../models/view-assignment';
|
||||||
import { ListViewBaseComponent } from '../../base/list-view-base';
|
import { ListViewBaseComponent } from '../../base/list-view-base';
|
||||||
import { AssignmentRepositoryService } from '../services/assignment-repository.service';
|
import { AssignmentRepositoryService } from '../services/assignment-repository.service';
|
||||||
|
import { MatSnackBar } from '@angular/material';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listview for the assignments
|
* Listview for the assignments
|
||||||
@ -18,12 +19,18 @@ export class AssignmentListComponent extends ListViewBaseComponent<ViewAssignmen
|
|||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param repo the repository
|
|
||||||
* @param titleService
|
* @param titleService
|
||||||
* @param translate
|
* @param translate
|
||||||
|
* @param matSnackBar
|
||||||
|
* @param repo the repository
|
||||||
*/
|
*/
|
||||||
public constructor(private repo: AssignmentRepositoryService, titleService: Title, translate: TranslateService) {
|
public constructor(
|
||||||
super(titleService, translate);
|
titleService: Title,
|
||||||
|
translate: TranslateService,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
|
private repo: AssignmentRepositoryService
|
||||||
|
) {
|
||||||
|
super(titleService, translate, matSnackBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
48
client/src/app/site/base/base-view.ts
Normal file
48
client/src/app/site/base/base-view.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { BaseComponent } from '../../base.component';
|
||||||
|
import { Title } from '@angular/platform-browser';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { MatSnackBar, MatSnackBarRef, SimpleSnackBar } from '@angular/material';
|
||||||
|
import { OnDestroy } from '@angular/core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A base class for all views. Implements a generic error handling by raising a snack bar
|
||||||
|
* with the error. The error is dismissed, if the component is destroyed, so if the
|
||||||
|
* view is leaved.
|
||||||
|
*/
|
||||||
|
export abstract class BaseViewComponent extends BaseComponent implements OnDestroy {
|
||||||
|
/**
|
||||||
|
* A reference to the current error snack bar.
|
||||||
|
*/
|
||||||
|
private errorSnackBar: MatSnackBarRef<SimpleSnackBar>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for bas elist views
|
||||||
|
* @param titleService the title serivce, passed to the base component
|
||||||
|
* @param translate the translate service, passed to the base component
|
||||||
|
* @param matSnackBar the snack bar service. Needed for showing errors.
|
||||||
|
*/
|
||||||
|
public constructor(titleService: Title, translate: TranslateService, private matSnackBar: MatSnackBar) {
|
||||||
|
super(titleService, translate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens an error snack bar with the given error message.
|
||||||
|
* This is implemented as an arrow function to capture the called `this`. You can use this function
|
||||||
|
* as callback (`.then(..., this.raiseError)`) instead of doing `this.raiseError.bind(this)`.
|
||||||
|
* @param message The message to show.
|
||||||
|
*/
|
||||||
|
protected raiseError = (message: string): void => {
|
||||||
|
this.errorSnackBar = this.matSnackBar.open(message, this.translate.instant('OK'), {
|
||||||
|
duration: 0
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* automatically dismisses the error snack bar, if the component is destroyed.
|
||||||
|
*/
|
||||||
|
public ngOnDestroy(): void {
|
||||||
|
if (this.errorSnackBar) {
|
||||||
|
this.errorSnackBar.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
import { ViewChild } from '@angular/core';
|
import { ViewChild } from '@angular/core';
|
||||||
import { BaseComponent } from '../../base.component';
|
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { MatTableDataSource, MatTable, MatSort, MatPaginator } from '@angular/material';
|
import { MatTableDataSource, MatTable, MatSort, MatPaginator, MatSnackBar } from '@angular/material';
|
||||||
import { BaseViewModel } from './base-view-model';
|
import { BaseViewModel } from './base-view-model';
|
||||||
|
import { BaseViewComponent } from './base-view';
|
||||||
|
|
||||||
export abstract class ListViewBaseComponent<V extends BaseViewModel> extends BaseComponent {
|
export abstract class ListViewBaseComponent<V extends BaseViewModel> extends BaseViewComponent {
|
||||||
/**
|
/**
|
||||||
* The data source for a table. Requires to be initialised with a BaseViewModel
|
* The data source for a table. Requires to be initialised with a BaseViewModel
|
||||||
*/
|
*/
|
||||||
@ -33,9 +33,10 @@ export abstract class ListViewBaseComponent<V extends BaseViewModel> extends Bas
|
|||||||
* Constructor for list view bases
|
* Constructor for list view bases
|
||||||
* @param titleService the title serivce
|
* @param titleService the title serivce
|
||||||
* @param translate the translate service
|
* @param translate the translate service
|
||||||
|
* @param matSnackBar
|
||||||
*/
|
*/
|
||||||
public constructor(titleService: Title, translate: TranslateService) {
|
public constructor(titleService: Title, translate: TranslateService, matSnackBar: MatSnackBar) {
|
||||||
super(titleService, translate);
|
super(titleService, translate, matSnackBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -115,20 +115,18 @@ export class ConfigFieldComponent extends BaseComponent implements OnInit {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the this config field.
|
* Updates the this config field.
|
||||||
|
* @param value The new value to set.
|
||||||
*/
|
*/
|
||||||
private async update(value: any): Promise<void> {
|
private update(value: any): void {
|
||||||
// TODO: Fix the Datetimepicker parser and formatter.
|
// TODO: Fix the Datetimepicker parser and formatter.
|
||||||
if (this.configItem.inputType === 'datetimepicker') {
|
if (this.configItem.inputType === 'datetimepicker') {
|
||||||
value = Date.parse(value);
|
value = Date.parse(value);
|
||||||
}
|
}
|
||||||
this.debounceTimeout = null;
|
this.debounceTimeout = null;
|
||||||
try {
|
this.repo.update({ value: value }, this.configItem).then(() => {
|
||||||
await this.repo.update({ value: value }, this.configItem);
|
|
||||||
this.error = null;
|
this.error = null;
|
||||||
this.showSuccessIcon();
|
this.showSuccessIcon();
|
||||||
} catch (e) {
|
}, this.setError.bind(this));
|
||||||
this.setError(e.error.detail);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,9 +90,10 @@ export class LoginMaskComponent extends BaseComponent implements OnInit, OnDestr
|
|||||||
* Observes the operator, if a user was already logged in, recreate to user and skip the login
|
* Observes the operator, if a user was already logged in, recreate to user and skip the login
|
||||||
*/
|
*/
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
// Get the login data. Save information to the login data service
|
// Get the login data. Save information to the login data service. If there is an
|
||||||
this.http.get<any>(environment.urlPrefix + '/users/login/').then(
|
// error, ignore it.
|
||||||
response => {
|
// TODO: This has to be caught by the offline service
|
||||||
|
this.http.get<any>(environment.urlPrefix + '/users/login/').then(response => {
|
||||||
if (response.info_text) {
|
if (response.info_text) {
|
||||||
this.installationNotice = this.matSnackBar.open(response.info_text, this.translate.instant('OK'), {
|
this.installationNotice = this.matSnackBar.open(response.info_text, this.translate.instant('OK'), {
|
||||||
duration: 5000
|
duration: 5000
|
||||||
@ -100,11 +101,7 @@ export class LoginMaskComponent extends BaseComponent implements OnInit, OnDestr
|
|||||||
}
|
}
|
||||||
this.loginDataService.setPrivacyPolicy(response.privacy_policy);
|
this.loginDataService.setPrivacyPolicy(response.privacy_policy);
|
||||||
this.loginDataService.setLegalNotice(response.legal_notice);
|
this.loginDataService.setLegalNotice(response.legal_notice);
|
||||||
},
|
}, () => {});
|
||||||
() => {
|
|
||||||
// TODO: Error handling
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy(): void {
|
public ngOnDestroy(): void {
|
||||||
|
@ -6,6 +6,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { ViewMediafile } from '../models/view-mediafile';
|
import { ViewMediafile } from '../models/view-mediafile';
|
||||||
import { MediafileRepositoryService } from '../services/mediafile-repository.service';
|
import { MediafileRepositoryService } from '../services/mediafile-repository.service';
|
||||||
import { ListViewBaseComponent } from '../../base/list-view-base';
|
import { ListViewBaseComponent } from '../../base/list-view-base';
|
||||||
|
import { MatSnackBar } from '@angular/material';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists all the uploaded files.
|
* Lists all the uploaded files.
|
||||||
@ -25,11 +26,12 @@ export class MediafileListComponent extends ListViewBaseComponent<ViewMediafile>
|
|||||||
* @param translate
|
* @param translate
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
private repo: MediafileRepositoryService,
|
titleService: Title,
|
||||||
protected titleService: Title,
|
translate: TranslateService,
|
||||||
protected translate: TranslateService
|
matSnackBar: MatSnackBar,
|
||||||
|
private repo: MediafileRepositoryService
|
||||||
) {
|
) {
|
||||||
super(titleService, translate);
|
super(titleService, translate, matSnackBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3,7 +3,6 @@ import { Title } from '@angular/platform-browser';
|
|||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { BaseComponent } from '../../../../base.component';
|
|
||||||
import { Category } from '../../../../shared/models/motions/category';
|
import { Category } from '../../../../shared/models/motions/category';
|
||||||
import { CategoryRepositoryService } from '../../services/category-repository.service';
|
import { CategoryRepositoryService } from '../../services/category-repository.service';
|
||||||
import { ViewCategory } from '../../models/view-category';
|
import { ViewCategory } from '../../models/view-category';
|
||||||
@ -11,6 +10,8 @@ import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
|||||||
import { Motion } from '../../../../shared/models/motions/motion';
|
import { Motion } from '../../../../shared/models/motions/motion';
|
||||||
import { SortingListComponent } from '../../../../shared/components/sorting-list/sorting-list.component';
|
import { SortingListComponent } from '../../../../shared/components/sorting-list/sorting-list.component';
|
||||||
import { PromptService } from 'app/core/services/prompt.service';
|
import { PromptService } from 'app/core/services/prompt.service';
|
||||||
|
import { BaseViewComponent } from '../../../base/base-view';
|
||||||
|
import { MatSnackBar } from '@angular/material';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List view for the categories.
|
* List view for the categories.
|
||||||
@ -20,7 +21,7 @@ import { PromptService } from 'app/core/services/prompt.service';
|
|||||||
templateUrl: './category-list.component.html',
|
templateUrl: './category-list.component.html',
|
||||||
styleUrls: ['./category-list.component.scss']
|
styleUrls: ['./category-list.component.scss']
|
||||||
})
|
})
|
||||||
export class CategoryListComponent extends BaseComponent implements OnInit {
|
export class CategoryListComponent extends BaseViewComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* Hold the category to create
|
* Hold the category to create
|
||||||
*/
|
*/
|
||||||
@ -56,17 +57,20 @@ export class CategoryListComponent extends BaseComponent implements OnInit {
|
|||||||
* The usual component constructor
|
* The usual component constructor
|
||||||
* @param titleService
|
* @param titleService
|
||||||
* @param translate
|
* @param translate
|
||||||
|
* @param matSnackBar
|
||||||
* @param repo
|
* @param repo
|
||||||
* @param formBuilder
|
* @param formBuilder
|
||||||
|
* @param promptService
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
protected titleService: Title,
|
titleService: Title,
|
||||||
protected translate: TranslateService,
|
translate: TranslateService,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
private repo: CategoryRepositoryService,
|
private repo: CategoryRepositoryService,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private promptService: PromptService
|
private promptService: PromptService
|
||||||
) {
|
) {
|
||||||
super(titleService, translate);
|
super(titleService, translate, matSnackBar);
|
||||||
|
|
||||||
this.createForm = this.formBuilder.group({
|
this.createForm = this.formBuilder.group({
|
||||||
prefix: ['', Validators.required],
|
prefix: ['', Validators.required],
|
||||||
@ -80,11 +84,12 @@ export class CategoryListComponent extends BaseComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event on Key Down in form
|
* Event on key-down in form
|
||||||
|
* @param event
|
||||||
|
* @param viewCategory
|
||||||
*/
|
*/
|
||||||
public keyDownFunction(event: KeyboardEvent, viewCategory?: ViewCategory): void {
|
public keyDownFunction(event: KeyboardEvent, viewCategory?: ViewCategory): void {
|
||||||
if (event.keyCode === 13) {
|
if (event.keyCode === 13) {
|
||||||
console.log('hit enter');
|
|
||||||
if (viewCategory) {
|
if (viewCategory) {
|
||||||
this.onSaveButton(viewCategory);
|
this.onSaveButton(viewCategory);
|
||||||
} else {
|
} else {
|
||||||
@ -119,11 +124,10 @@ export class CategoryListComponent extends BaseComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* Creates a new category. Executed after hitting save.
|
* Creates a new category. Executed after hitting save.
|
||||||
*/
|
*/
|
||||||
public async onCreateButton(): Promise<void> {
|
public onCreateButton(): void {
|
||||||
if (this.createForm.valid) {
|
if (this.createForm.valid) {
|
||||||
this.categoryToCreate.patchValues(this.createForm.value as Category);
|
this.categoryToCreate.patchValues(this.createForm.value as Category);
|
||||||
await this.repo.create(this.categoryToCreate)
|
this.repo.create(this.categoryToCreate).then(() => (this.categoryToCreate = null), this.raiseError);
|
||||||
this.categoryToCreate = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,11 +145,17 @@ export class CategoryListComponent extends BaseComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the categories
|
* Saves the category
|
||||||
|
* @param viewCategory
|
||||||
*/
|
*/
|
||||||
public async onSaveButton(viewCategory: ViewCategory): Promise<void> {
|
public async onSaveButton(viewCategory: ViewCategory): Promise<void> {
|
||||||
if (this.updateForm.valid) {
|
if (this.updateForm.valid) {
|
||||||
|
// TODO: Check the motion sorting code below. If it is removed, change to .then() syntax.
|
||||||
|
try {
|
||||||
await this.repo.update(this.updateForm.value as Partial<Category>, viewCategory);
|
await this.repo.update(this.updateForm.value as Partial<Category>, viewCategory);
|
||||||
|
} catch (e) {
|
||||||
|
this.raiseError(e);
|
||||||
|
}
|
||||||
this.onCancelButton();
|
this.onCancelButton();
|
||||||
this.sortDataSource();
|
this.sortDataSource();
|
||||||
}
|
}
|
||||||
@ -174,12 +184,12 @@ export class CategoryListComponent extends BaseComponent implements OnInit {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* is executed, when the delete button is pressed
|
* is executed, when the delete button is pressed
|
||||||
|
* @param viewCategory The category to delete
|
||||||
*/
|
*/
|
||||||
public async onDeleteButton(viewCategory: ViewCategory): Promise<void> {
|
public async onDeleteButton(viewCategory: ViewCategory): Promise<void> {
|
||||||
const content = this.translate.instant('Delete') + ` ${viewCategory.name}?`;
|
const content = this.translate.instant('Delete') + ` ${viewCategory.name}?`;
|
||||||
if (await this.promptService.open('Are you sure?', content)) {
|
if (await this.promptService.open('Are you sure?', content)) {
|
||||||
await this.repo.delete(viewCategory);
|
this.repo.delete(viewCategory).then(() => this.onCancelButton(), this.raiseError);
|
||||||
this.onCancelButton();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
<button mat-button mat-dialog-close>Abort</button>
|
<button mat-button mat-dialog-close translate>Abort</button>
|
||||||
<!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. -->
|
<!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. -->
|
||||||
<button mat-button (click)="saveChangeRecommendation()">save</button>
|
<button mat-button (click)="saveChangeRecommendation()" translate>Save</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import { LineRange, ModificationType } from '../../services/diff.service';
|
import { LineRange, ModificationType } from '../../services/diff.service';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
|
import { MAT_DIALOG_DATA, MatDialogRef, MatSnackBar } from '@angular/material';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { ChangeRecommendationRepositoryService } from '../../services/change-recommendation-repository.service';
|
import { ChangeRecommendationRepositoryService } from '../../services/change-recommendation-repository.service';
|
||||||
import { ViewChangeReco } from '../../models/view-change-reco';
|
import { ViewChangeReco } from '../../models/view-change-reco';
|
||||||
|
import { BaseViewComponent } from '../../../base/base-view';
|
||||||
|
import { Title } from '@angular/platform-browser';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data that needs to be provided to the MotionChangeRecommendationComponent dialog
|
* Data that needs to be provided to the MotionChangeRecommendationComponent dialog
|
||||||
@ -39,7 +42,7 @@ export interface MotionChangeRecommendationComponentData {
|
|||||||
templateUrl: './motion-change-recommendation.component.html',
|
templateUrl: './motion-change-recommendation.component.html',
|
||||||
styleUrls: ['./motion-change-recommendation.component.scss']
|
styleUrls: ['./motion-change-recommendation.component.scss']
|
||||||
})
|
})
|
||||||
export class MotionChangeRecommendationComponent {
|
export class MotionChangeRecommendationComponent extends BaseViewComponent {
|
||||||
/**
|
/**
|
||||||
* Determine if the change recommendation is edited
|
* Determine if the change recommendation is edited
|
||||||
*/
|
*/
|
||||||
@ -86,10 +89,15 @@ export class MotionChangeRecommendationComponent {
|
|||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
@Inject(MAT_DIALOG_DATA) public data: MotionChangeRecommendationComponentData,
|
@Inject(MAT_DIALOG_DATA) public data: MotionChangeRecommendationComponentData,
|
||||||
|
title: Title,
|
||||||
|
translate: TranslateService,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private repo: ChangeRecommendationRepositoryService,
|
private repo: ChangeRecommendationRepositoryService,
|
||||||
private dialogRef: MatDialogRef<MotionChangeRecommendationComponent>
|
private dialogRef: MatDialogRef<MotionChangeRecommendationComponent>
|
||||||
) {
|
) {
|
||||||
|
super(title, translate, matSnackBar);
|
||||||
|
|
||||||
this.editReco = data.editChangeRecommendation;
|
this.editReco = data.editChangeRecommendation;
|
||||||
this.newReco = data.newChangeRecommendation;
|
this.newReco = data.newChangeRecommendation;
|
||||||
this.changeReco = data.changeRecommendation;
|
this.changeReco = data.changeRecommendation;
|
||||||
@ -116,14 +124,16 @@ export class MotionChangeRecommendationComponent {
|
|||||||
!this.contentForm.controls.public.value
|
!this.contentForm.controls.public.value
|
||||||
);
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
if (this.newReco) {
|
if (this.newReco) {
|
||||||
await this.repo.createByViewModel(this.changeReco);
|
await this.repo.createByViewModel(this.changeReco);
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
// @TODO Show an error message
|
|
||||||
} else {
|
} else {
|
||||||
await this.repo.update(this.changeReco.changeRecommendation, this.changeReco);
|
await this.repo.update(this.changeReco.changeRecommendation, this.changeReco);
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
// @TODO Show an error message
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.raiseError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<div class="write">
|
<div class="write">
|
||||||
<mat-icon>add</mat-icon>
|
<mat-icon>edit</mat-icon>
|
||||||
{{ section.write_groups }}
|
{{ section.write_groups }}
|
||||||
<ng-container *ngIf="section.write_groups.length === 0">
|
<ng-container *ngIf="section.write_groups.length === 0">
|
||||||
–
|
–
|
||||||
|
@ -3,7 +3,6 @@ import { Title } from '@angular/platform-browser';
|
|||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { BaseComponent } from '../../../../base.component';
|
|
||||||
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||||
import { MotionCommentSection } from '../../../../shared/models/motions/motion-comment-section';
|
import { MotionCommentSection } from '../../../../shared/models/motions/motion-comment-section';
|
||||||
import { ViewMotionCommentSection } from '../../models/view-motion-comment-section';
|
import { ViewMotionCommentSection } from '../../models/view-motion-comment-section';
|
||||||
@ -12,6 +11,8 @@ import { PromptService } from '../../../../core/services/prompt.service';
|
|||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject } from 'rxjs';
|
||||||
import { Group } from '../../../../shared/models/users/group';
|
import { Group } from '../../../../shared/models/users/group';
|
||||||
import { DataStoreService } from '../../../../core/services/data-store.service';
|
import { DataStoreService } from '../../../../core/services/data-store.service';
|
||||||
|
import { BaseViewComponent } from '../../../base/base-view';
|
||||||
|
import { MatSnackBar } from '@angular/material';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List view for the categories.
|
* List view for the categories.
|
||||||
@ -21,7 +22,7 @@ import { DataStoreService } from '../../../../core/services/data-store.service';
|
|||||||
templateUrl: './motion-comment-section-list.component.html',
|
templateUrl: './motion-comment-section-list.component.html',
|
||||||
styleUrls: ['./motion-comment-section-list.component.scss']
|
styleUrls: ['./motion-comment-section-list.component.scss']
|
||||||
})
|
})
|
||||||
export class MotionCommentSectionListComponent extends BaseComponent implements OnInit {
|
export class MotionCommentSectionListComponent extends BaseViewComponent implements OnInit {
|
||||||
public commentSectionToCreate: MotionCommentSection | null;
|
public commentSectionToCreate: MotionCommentSection | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,18 +46,23 @@ export class MotionCommentSectionListComponent extends BaseComponent implements
|
|||||||
* The usual component constructor
|
* The usual component constructor
|
||||||
* @param titleService
|
* @param titleService
|
||||||
* @param translate
|
* @param translate
|
||||||
|
* @param matSnackBar
|
||||||
* @param repo
|
* @param repo
|
||||||
* @param formBuilder
|
* @param formBuilder
|
||||||
|
* @param promptService
|
||||||
|
* @param DS
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
protected titleService: Title,
|
titleService: Title,
|
||||||
protected translate: TranslateService,
|
translate: TranslateService,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
private repo: MotionCommentSectionRepositoryService,
|
private repo: MotionCommentSectionRepositoryService,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private promptService: PromptService,
|
private promptService: PromptService,
|
||||||
private DS: DataStoreService
|
private DS: DataStoreService
|
||||||
) {
|
) {
|
||||||
super(titleService, translate);
|
super(titleService, translate, matSnackBar);
|
||||||
|
|
||||||
const form = {
|
const form = {
|
||||||
name: ['', Validators.required],
|
name: ['', Validators.required],
|
||||||
read_groups_id: [[]],
|
read_groups_id: [[]],
|
||||||
@ -98,7 +104,7 @@ export class MotionCommentSectionListComponent extends BaseComponent implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new Section.
|
* Opens the create form.
|
||||||
*/
|
*/
|
||||||
public onPlusButton(): void {
|
public onPlusButton(): void {
|
||||||
if (!this.commentSectionToCreate) {
|
if (!this.commentSectionToCreate) {
|
||||||
@ -111,11 +117,15 @@ export class MotionCommentSectionListComponent extends BaseComponent implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async create(): Promise<void> {
|
/**
|
||||||
|
* Creates the comment section from the create form.
|
||||||
|
*/
|
||||||
|
public create(): void {
|
||||||
if (this.createForm.valid) {
|
if (this.createForm.valid) {
|
||||||
this.commentSectionToCreate.patchValues(this.createForm.value as MotionCommentSection);
|
this.commentSectionToCreate.patchValues(this.createForm.value as MotionCommentSection);
|
||||||
await this.repo.create(this.commentSectionToCreate);
|
this.repo
|
||||||
this.commentSectionToCreate = null;
|
.create(this.commentSectionToCreate)
|
||||||
|
.then(() => (this.commentSectionToCreate = null), this.raiseError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,22 +145,24 @@ export class MotionCommentSectionListComponent extends BaseComponent implements
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the categories
|
* Saves the categories
|
||||||
|
* @param viewSection The section to save
|
||||||
*/
|
*/
|
||||||
public async onSaveButton(viewSection: ViewMotionCommentSection): Promise<void> {
|
public onSaveButton(viewSection: ViewMotionCommentSection): void {
|
||||||
if (this.updateForm.valid) {
|
if (this.updateForm.valid) {
|
||||||
await this.repo.update(this.updateForm.value as Partial<MotionCommentSection>, viewSection);
|
this.repo.update(this.updateForm.value as Partial<MotionCommentSection>, viewSection).then(() => {
|
||||||
this.openId = this.editId = null;
|
this.openId = this.editId = null;
|
||||||
|
}, this.raiseError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* is executed, when the delete button is pressed
|
* is executed, when the delete button is pressed
|
||||||
|
* @param viewSection The section to delete
|
||||||
*/
|
*/
|
||||||
public async onDeleteButton(viewSection: ViewMotionCommentSection): Promise<void> {
|
public async onDeleteButton(viewSection: ViewMotionCommentSection): Promise<void> {
|
||||||
const content = this.translate.instant('Delete') + ` ${viewSection.name}?`;
|
const content = this.translate.instant('Delete') + ` ${viewSection.name}?`;
|
||||||
if (await this.promptService.open('Are you sure?', content)) {
|
if (await this.promptService.open('Are you sure?', content)) {
|
||||||
await this.repo.delete(viewSection);
|
this.repo.delete(viewSection).then(() => (this.openId = this.editId = null), this.raiseError);
|
||||||
this.openId = this.editId = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
import { AfterViewInit, Component, ElementRef, EventEmitter, Input, Output } from '@angular/core';
|
import { AfterViewInit, Component, ElementRef, EventEmitter, Input, Output } from '@angular/core';
|
||||||
import { ViewMotion } from '../../models/view-motion';
|
import { ViewMotion } from '../../models/view-motion';
|
||||||
import { ViewUnifiedChange, ViewUnifiedChangeType } from '../../models/view-unified-change';
|
import { ViewUnifiedChange, ViewUnifiedChangeType } from '../../models/view-unified-change';
|
||||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
import { DomSanitizer, SafeHtml, Title } from '@angular/platform-browser';
|
||||||
import { MotionRepositoryService } from '../../services/motion-repository.service';
|
import { MotionRepositoryService } from '../../services/motion-repository.service';
|
||||||
import { LineRange, ModificationType } from '../../services/diff.service';
|
import { LineRange, ModificationType } from '../../services/diff.service';
|
||||||
import { ViewChangeReco } from '../../models/view-change-reco';
|
import { ViewChangeReco } from '../../models/view-change-reco';
|
||||||
import { MatDialog } from '@angular/material';
|
import { MatDialog, MatSnackBar } from '@angular/material';
|
||||||
import { ChangeRecommendationRepositoryService } from '../../services/change-recommendation-repository.service';
|
import { ChangeRecommendationRepositoryService } from '../../services/change-recommendation-repository.service';
|
||||||
import {
|
import {
|
||||||
MotionChangeRecommendationComponent,
|
MotionChangeRecommendationComponent,
|
||||||
MotionChangeRecommendationComponentData
|
MotionChangeRecommendationComponentData
|
||||||
} from '../motion-change-recommendation/motion-change-recommendation.component';
|
} from '../motion-change-recommendation/motion-change-recommendation.component';
|
||||||
|
import { BaseViewComponent } from '../../../base/base-view';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component displays the original motion text with the change blocks inside.
|
* This component displays the original motion text with the change blocks inside.
|
||||||
@ -36,7 +38,7 @@ import {
|
|||||||
templateUrl: './motion-detail-diff.component.html',
|
templateUrl: './motion-detail-diff.component.html',
|
||||||
styleUrls: ['./motion-detail-diff.component.scss']
|
styleUrls: ['./motion-detail-diff.component.scss']
|
||||||
})
|
})
|
||||||
export class MotionDetailDiffComponent implements AfterViewInit {
|
export class MotionDetailDiffComponent extends BaseViewComponent implements AfterViewInit {
|
||||||
@Input()
|
@Input()
|
||||||
public motion: ViewMotion;
|
public motion: ViewMotion;
|
||||||
@Input()
|
@Input()
|
||||||
@ -47,13 +49,28 @@ export class MotionDetailDiffComponent implements AfterViewInit {
|
|||||||
@Output()
|
@Output()
|
||||||
public createChangeRecommendation: EventEmitter<LineRange> = new EventEmitter<LineRange>();
|
public createChangeRecommendation: EventEmitter<LineRange> = new EventEmitter<LineRange>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param title
|
||||||
|
* @param translate
|
||||||
|
* @param matSnackBar
|
||||||
|
* @param sanitizer
|
||||||
|
* @param motionRepo
|
||||||
|
* @param recoRepo
|
||||||
|
* @param dialogService
|
||||||
|
* @param el
|
||||||
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
|
title: Title,
|
||||||
|
translate: TranslateService,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
private sanitizer: DomSanitizer,
|
private sanitizer: DomSanitizer,
|
||||||
private motionRepo: MotionRepositoryService,
|
private motionRepo: MotionRepositoryService,
|
||||||
private recoRepo: ChangeRecommendationRepositoryService,
|
private recoRepo: ChangeRecommendationRepositoryService,
|
||||||
private dialogService: MatDialog,
|
private dialogService: MatDialog,
|
||||||
private el: ElementRef
|
private el: ElementRef
|
||||||
) {}
|
) {
|
||||||
|
super(title, translate, matSnackBar);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the part of this motion between two change objects
|
* Returns the part of this motion between two change objects
|
||||||
@ -171,12 +188,16 @@ export class MotionDetailDiffComponent implements AfterViewInit {
|
|||||||
* @param {string} value
|
* @param {string} value
|
||||||
*/
|
*/
|
||||||
public async setAcceptanceValue(change: ViewChangeReco, value: string): Promise<void> {
|
public async setAcceptanceValue(change: ViewChangeReco, value: string): Promise<void> {
|
||||||
|
try {
|
||||||
if (value === 'accepted') {
|
if (value === 'accepted') {
|
||||||
await this.recoRepo.setAccepted(change);
|
await this.recoRepo.setAccepted(change);
|
||||||
}
|
}
|
||||||
if (value === 'rejected') {
|
if (value === 'rejected') {
|
||||||
await this.recoRepo.setRejected(change);
|
await this.recoRepo.setRejected(change);
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.raiseError(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -185,8 +206,8 @@ export class MotionDetailDiffComponent implements AfterViewInit {
|
|||||||
* @param {ViewChangeReco} change
|
* @param {ViewChangeReco} change
|
||||||
* @param {boolean} internal
|
* @param {boolean} internal
|
||||||
*/
|
*/
|
||||||
public async setInternal(change: ViewChangeReco, internal: boolean): Promise<void> {
|
public setInternal(change: ViewChangeReco, internal: boolean): void {
|
||||||
await this.recoRepo.setInternal(change, internal);
|
this.recoRepo.setInternal(change, internal).then(null, this.raiseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -196,10 +217,10 @@ export class MotionDetailDiffComponent implements AfterViewInit {
|
|||||||
* @param {ViewChangeReco} reco
|
* @param {ViewChangeReco} reco
|
||||||
* @param {MouseEvent} $event
|
* @param {MouseEvent} $event
|
||||||
*/
|
*/
|
||||||
public async deleteChangeRecommendation(reco: ViewChangeReco, $event: MouseEvent): Promise<void> {
|
public deleteChangeRecommendation(reco: ViewChangeReco, $event: MouseEvent): void {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
$event.preventDefault();
|
$event.preventDefault();
|
||||||
await this.recoRepo.delete(reco);
|
this.recoRepo.delete(reco).then(null, this.raiseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { MatDialog, MatExpansionPanel, MatSelectChange } from '@angular/material';
|
import { MatDialog, MatExpansionPanel, MatSnackBar, MatSelectChange } from '@angular/material';
|
||||||
|
|
||||||
import { BaseComponent } from '../../../../base.component';
|
|
||||||
import { Category } from '../../../../shared/models/motions/category';
|
import { Category } from '../../../../shared/models/motions/category';
|
||||||
import { ViewportService } from '../../../../core/services/viewport.service';
|
import { ViewportService } from '../../../../core/services/viewport.service';
|
||||||
import { MotionRepositoryService } from '../../services/motion-repository.service';
|
import { MotionRepositoryService } from '../../services/motion-repository.service';
|
||||||
@ -20,10 +19,10 @@ import {
|
|||||||
} from '../motion-change-recommendation/motion-change-recommendation.component';
|
} from '../motion-change-recommendation/motion-change-recommendation.component';
|
||||||
import { ChangeRecommendationRepositoryService } from '../../services/change-recommendation-repository.service';
|
import { ChangeRecommendationRepositoryService } from '../../services/change-recommendation-repository.service';
|
||||||
import { ViewChangeReco } from '../../models/view-change-reco';
|
import { ViewChangeReco } from '../../models/view-change-reco';
|
||||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
import { DomSanitizer, SafeHtml, Title } from '@angular/platform-browser';
|
||||||
import { ViewUnifiedChange } from '../../models/view-unified-change';
|
import { ViewUnifiedChange } from '../../models/view-unified-change';
|
||||||
import { OperatorService } from '../../../../core/services/operator.service';
|
import { OperatorService } from '../../../../core/services/operator.service';
|
||||||
import { CategoryRepositoryService } from '../../services/category-repository.service';
|
import { BaseViewComponent } from '../../../base/base-view';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for the motion detail view
|
* Component for the motion detail view
|
||||||
@ -33,7 +32,7 @@ import { CategoryRepositoryService } from '../../services/category-repository.se
|
|||||||
templateUrl: './motion-detail.component.html',
|
templateUrl: './motion-detail.component.html',
|
||||||
styleUrls: ['./motion-detail.component.scss']
|
styleUrls: ['./motion-detail.component.scss']
|
||||||
})
|
})
|
||||||
export class MotionDetailComponent extends BaseComponent implements OnInit {
|
export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* MatExpansionPanel for the meta info
|
* MatExpansionPanel for the meta info
|
||||||
* Only relevant in mobile view
|
* Only relevant in mobile view
|
||||||
@ -131,18 +130,24 @@ export class MotionDetailComponent extends BaseComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* Constuct the detail view.
|
* Constuct the detail view.
|
||||||
*
|
*
|
||||||
|
* @param title
|
||||||
|
* @param translate
|
||||||
|
* @param matSnackBar
|
||||||
* @param vp the viewport service
|
* @param vp the viewport service
|
||||||
|
* @param op
|
||||||
* @param router to navigate back to the motion list and to an existing motion
|
* @param router to navigate back to the motion list and to an existing motion
|
||||||
* @param route determine if this is a new or an existing motion
|
* @param route determine if this is a new or an existing motion
|
||||||
* @param formBuilder For reactive forms. Form Group and Form Control
|
* @param formBuilder For reactive forms. Form Group and Form Control
|
||||||
* @param dialogService For opening dialogs
|
* @param dialogService For opening dialogs
|
||||||
* @param repo: Motion Repository
|
* @param repo Motion Repository
|
||||||
* @param changeRecoRepo: Change Recommendation Repository
|
* @param changeRecoRepo Change Recommendation Repository
|
||||||
* @param DS: The DataStoreService
|
* @param DS The DataStoreService
|
||||||
* @param sanitizer: For making HTML SafeHTML
|
* @param sanitizer For making HTML SafeHTML
|
||||||
* @param translate: Translation Service
|
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
|
title: Title,
|
||||||
|
translate: TranslateService,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
public vp: ViewportService,
|
public vp: ViewportService,
|
||||||
private op: OperatorService,
|
private op: OperatorService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
@ -151,12 +156,10 @@ export class MotionDetailComponent extends BaseComponent implements OnInit {
|
|||||||
private dialogService: MatDialog,
|
private dialogService: MatDialog,
|
||||||
private repo: MotionRepositoryService,
|
private repo: MotionRepositoryService,
|
||||||
private changeRecoRepo: ChangeRecommendationRepositoryService,
|
private changeRecoRepo: ChangeRecommendationRepositoryService,
|
||||||
private categoryRepo: CategoryRepositoryService,
|
|
||||||
private DS: DataStoreService,
|
private DS: DataStoreService,
|
||||||
private sanitizer: DomSanitizer,
|
private sanitizer: DomSanitizer
|
||||||
protected translate: TranslateService
|
|
||||||
) {
|
) {
|
||||||
super();
|
super(title, translate, matSnackBar);
|
||||||
this.createForm();
|
this.createForm();
|
||||||
this.getMotionByUrl();
|
this.getMotionByUrl();
|
||||||
|
|
||||||
@ -278,6 +281,7 @@ export class MotionDetailComponent extends BaseComponent implements OnInit {
|
|||||||
const fromForm = new Motion();
|
const fromForm = new Motion();
|
||||||
fromForm.deserialize(newMotionValues);
|
fromForm.deserialize(newMotionValues);
|
||||||
|
|
||||||
|
try {
|
||||||
if (this.newMotion) {
|
if (this.newMotion) {
|
||||||
const response = await this.repo.create(fromForm);
|
const response = await this.repo.create(fromForm);
|
||||||
this.router.navigate(['./motions/' + response.id]);
|
this.router.navigate(['./motions/' + response.id]);
|
||||||
@ -285,7 +289,9 @@ export class MotionDetailComponent extends BaseComponent implements OnInit {
|
|||||||
await this.repo.update(fromForm, this.motionCopy);
|
await this.repo.update(fromForm, this.motionCopy);
|
||||||
// if the motion was successfully updated, change the edit mode.
|
// if the motion was successfully updated, change the edit mode.
|
||||||
this.editMotion = false;
|
this.editMotion = false;
|
||||||
// TODO: Show errors if there appear here
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.raiseError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,13 +325,14 @@ export class MotionDetailComponent extends BaseComponent implements OnInit {
|
|||||||
public deleteMotionButton(): void {
|
public deleteMotionButton(): void {
|
||||||
this.repo.delete(this.motion).then(() => {
|
this.repo.delete(this.motion).then(() => {
|
||||||
this.router.navigate(['./motions/']);
|
this.router.navigate(['./motions/']);
|
||||||
});
|
}, this.raiseError);
|
||||||
const motList = this.categoryRepo.getMotionsOfCategory(this.motion.category);
|
// TODO: this needs to be in the autoupdate code.
|
||||||
|
/*const motList = this.categoryRepo.getMotionsOfCategory(this.motion.category);
|
||||||
const index = motList.indexOf(this.motion.motion, 0);
|
const index = motList.indexOf(this.motion.motion, 0);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
motList.splice(index, 1);
|
motList.splice(index, 1);
|
||||||
}
|
}
|
||||||
this.categoryRepo.updateCategoryNumbering(this.motion.category, motList);
|
this.categoryRepo.updateCategoryNumbering(this.motion.category, motList);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
<mat-table class='os-listview-table on-transition-fade' [dataSource]="dataSource" matSort>
|
<mat-table class='os-listview-table on-transition-fade' [dataSource]="dataSource" matSort>
|
||||||
<!-- identifier column -->
|
<!-- identifier column -->
|
||||||
<ng-container matColumnDef="identifier">
|
<ng-container matColumnDef="identifier">
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header> Identifier </mat-header-cell>
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Identifier</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let motion">
|
<mat-cell *matCellDef="let motion">
|
||||||
<div class='innerTable'>
|
<div class='innerTable'>
|
||||||
{{motion.identifier}}
|
{{motion.identifier}}
|
||||||
@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
<!-- title column -->
|
<!-- title column -->
|
||||||
<ng-container matColumnDef="title">
|
<ng-container matColumnDef="title">
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header> Title </mat-header-cell>
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Title</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let motion">
|
<mat-cell *matCellDef="let motion">
|
||||||
<div class='innerTable'>
|
<div class='innerTable'>
|
||||||
<span class='motion-list-title'>{{motion.title}}</span>
|
<span class='motion-list-title'>{{motion.title}}</span>
|
||||||
@ -49,7 +49,7 @@
|
|||||||
|
|
||||||
<!-- state column -->
|
<!-- state column -->
|
||||||
<ng-container matColumnDef="state">
|
<ng-container matColumnDef="state">
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header> State </mat-header-cell>
|
<mat-header-cell *matHeaderCellDef mat-sort-header>State</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let motion">
|
<mat-cell *matCellDef="let motion">
|
||||||
<div *ngIf='isDisplayIcon(motion.state) && motion.state' class='innerTable'>
|
<div *ngIf='isDisplayIcon(motion.state) && motion.state' class='innerTable'>
|
||||||
<mat-icon>{{getStateIcon(motion.state)}}></mat-icon>
|
<mat-icon>{{getStateIcon(motion.state)}}></mat-icon>
|
||||||
@ -77,7 +77,7 @@
|
|||||||
|
|
||||||
<button mat-menu-item routerLink="comment-section">
|
<button mat-menu-item routerLink="comment-section">
|
||||||
<mat-icon>speaker_notes</mat-icon>
|
<mat-icon>speaker_notes</mat-icon>
|
||||||
<span translate>Comments</span>
|
<span translate>Comment sections</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button mat-menu-item routerLink="statute-paragraphs">
|
<button mat-menu-item routerLink="statute-paragraphs">
|
||||||
|
@ -8,6 +8,7 @@ import { MotionRepositoryService } from '../../services/motion-repository.servic
|
|||||||
import { ViewMotion } from '../../models/view-motion';
|
import { ViewMotion } from '../../models/view-motion';
|
||||||
import { WorkflowState } from '../../../../shared/models/motions/workflow-state';
|
import { WorkflowState } from '../../../../shared/models/motions/workflow-state';
|
||||||
import { ListViewBaseComponent } from '../../../base/list-view-base';
|
import { ListViewBaseComponent } from '../../../base/list-view-base';
|
||||||
|
import { MatSnackBar } from '@angular/material';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays all the motions in a Table using DataSource.
|
* Component that displays all the motions in a Table using DataSource.
|
||||||
@ -42,11 +43,12 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
|||||||
public constructor(
|
public constructor(
|
||||||
titleService: Title,
|
titleService: Title,
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private repo: MotionRepositoryService
|
private repo: MotionRepositoryService
|
||||||
) {
|
) {
|
||||||
super(titleService, translate);
|
super(titleService, translate, matSnackBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3,12 +3,13 @@ import { Title } from '@angular/platform-browser';
|
|||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { BaseComponent } from '../../../../base.component';
|
|
||||||
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||||
import { PromptService } from '../../../../core/services/prompt.service';
|
import { PromptService } from '../../../../core/services/prompt.service';
|
||||||
import { StatuteParagraph } from '../../../../shared/models/motions/statute-paragraph';
|
import { StatuteParagraph } from '../../../../shared/models/motions/statute-paragraph';
|
||||||
import { ViewStatuteParagraph } from '../../models/view-statute-paragraph';
|
import { ViewStatuteParagraph } from '../../models/view-statute-paragraph';
|
||||||
import { StatuteParagraphRepositoryService } from '../../services/statute-paragraph-repository.service';
|
import { StatuteParagraphRepositoryService } from '../../services/statute-paragraph-repository.service';
|
||||||
|
import { BaseViewComponent } from '../../../base/base-view';
|
||||||
|
import { MatSnackBar } from '@angular/material';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List view for the statute paragraphs.
|
* List view for the statute paragraphs.
|
||||||
@ -18,7 +19,7 @@ import { StatuteParagraphRepositoryService } from '../../services/statute-paragr
|
|||||||
templateUrl: './statute-paragraph-list.component.html',
|
templateUrl: './statute-paragraph-list.component.html',
|
||||||
styleUrls: ['./statute-paragraph-list.component.scss']
|
styleUrls: ['./statute-paragraph-list.component.scss']
|
||||||
})
|
})
|
||||||
export class StatuteParagraphListComponent extends BaseComponent implements OnInit {
|
export class StatuteParagraphListComponent extends BaseViewComponent implements OnInit {
|
||||||
public statuteParagraphToCreate: StatuteParagraph | null;
|
public statuteParagraphToCreate: StatuteParagraph | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,17 +41,21 @@ export class StatuteParagraphListComponent extends BaseComponent implements OnIn
|
|||||||
* The usual component constructor
|
* The usual component constructor
|
||||||
* @param titleService
|
* @param titleService
|
||||||
* @param translate
|
* @param translate
|
||||||
|
* @param matSnackBar
|
||||||
* @param repo
|
* @param repo
|
||||||
* @param formBuilder
|
* @param formBuilder
|
||||||
|
* @param promptService
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
protected titleService: Title,
|
titleService: Title,
|
||||||
protected translate: TranslateService,
|
translate: TranslateService,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
private repo: StatuteParagraphRepositoryService,
|
private repo: StatuteParagraphRepositoryService,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private promptService: PromptService
|
private promptService: PromptService
|
||||||
) {
|
) {
|
||||||
super(titleService, translate);
|
super(titleService, translate, matSnackBar);
|
||||||
|
|
||||||
const form = {
|
const form = {
|
||||||
title: ['', Validators.required],
|
title: ['', Validators.required],
|
||||||
text: ['', Validators.required]
|
text: ['', Validators.required]
|
||||||
@ -85,11 +90,15 @@ export class StatuteParagraphListComponent extends BaseComponent implements OnIn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async create(): Promise<void> {
|
/**
|
||||||
|
* Handler when clicking on create to create a new statute paragraph
|
||||||
|
*/
|
||||||
|
public create(): void {
|
||||||
if (this.createForm.valid) {
|
if (this.createForm.valid) {
|
||||||
this.statuteParagraphToCreate.patchValues(this.createForm.value as StatuteParagraph);
|
this.statuteParagraphToCreate.patchValues(this.createForm.value as StatuteParagraph);
|
||||||
await this.repo.create(this.statuteParagraphToCreate);
|
this.repo.create(this.statuteParagraphToCreate).then(() => {
|
||||||
this.statuteParagraphToCreate = null;
|
this.statuteParagraphToCreate = null;
|
||||||
|
}, this.raiseError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,23 +116,25 @@ export class StatuteParagraphListComponent extends BaseComponent implements OnIn
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the statute paragrpah
|
* Saves the statute paragraph
|
||||||
|
* @param viewStatuteParagraph The statute paragraph to save
|
||||||
*/
|
*/
|
||||||
public async onSaveButton(viewStatuteParagraph: ViewStatuteParagraph): Promise<void> {
|
public onSaveButton(viewStatuteParagraph: ViewStatuteParagraph): void {
|
||||||
if (this.updateForm.valid) {
|
if (this.updateForm.valid) {
|
||||||
await this.repo.update(this.updateForm.value as Partial<StatuteParagraph>, viewStatuteParagraph);
|
this.repo.update(this.updateForm.value as Partial<StatuteParagraph>, viewStatuteParagraph).then(() => {
|
||||||
this.openId = this.editId = null;
|
this.openId = this.editId = null;
|
||||||
|
}, this.raiseError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* is executed, when the delete button is pressed
|
* Is executed, when the delete button is pressed
|
||||||
|
* @param viewStatuteParagraph The statute paragraph to delete
|
||||||
*/
|
*/
|
||||||
public async onDeleteButton(viewStatuteParagraph: ViewStatuteParagraph): Promise<void> {
|
public async onDeleteButton(viewStatuteParagraph: ViewStatuteParagraph): Promise<void> {
|
||||||
const content = this.translate.instant('Delete') + ` ${viewStatuteParagraph.title}?`;
|
const content = this.translate.instant('Delete') + ` ${viewStatuteParagraph.title}?`;
|
||||||
if (await this.promptService.open('Are you sure?', content)) {
|
if (await this.promptService.open('Are you sure?', content)) {
|
||||||
await this.repo.delete(viewStatuteParagraph);
|
this.repo.delete(viewStatuteParagraph).then(() => (this.openId = this.editId = null), this.raiseError);
|
||||||
this.openId = this.editId = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,17 +54,12 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<ViewCh
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a change recommendation view object, a entry in the backend is created and the new
|
* Given a change recommendation view object, a entry in the backend is created.
|
||||||
* change recommendation view object is returned (as an observable).
|
* @param view
|
||||||
*
|
* @returns The id of the created change recommendation
|
||||||
* @param {ViewChangeReco} view
|
|
||||||
* @deprecated Will not work with PR #3928. There will just be the id as response to create requests.
|
|
||||||
* Two possibilities: Make a server change to still retrieve the created object or you have to wait for the
|
|
||||||
* correct autoupdate.
|
|
||||||
*/
|
*/
|
||||||
public async createByViewModel(view: ViewChangeReco): Promise<Identifiable> {
|
public async createByViewModel(view: ViewChangeReco): Promise<Identifiable> {
|
||||||
return await this.dataSend.createModel(view.changeRecommendation);
|
return await this.dataSend.createModel(view.changeRecommendation);
|
||||||
// return new ViewChangeReco(cr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,6 +7,7 @@ import { TagRepositoryService } from '../../services/tag-repository.service';
|
|||||||
import { ViewTag } from '../../models/view-tag';
|
import { ViewTag } from '../../models/view-tag';
|
||||||
import { FormGroup, FormControl, Validators } from '@angular/forms';
|
import { FormGroup, FormControl, Validators } from '@angular/forms';
|
||||||
import { PromptService } from '../../../../core/services/prompt.service';
|
import { PromptService } from '../../../../core/services/prompt.service';
|
||||||
|
import { MatSnackBar } from '@angular/material';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listview for the complete lsit of available Tags
|
* Listview for the complete lsit of available Tags
|
||||||
@ -32,15 +33,18 @@ export class TagListComponent extends ListViewBaseComponent<ViewTag> implements
|
|||||||
* Constructor.
|
* Constructor.
|
||||||
* @param titleService
|
* @param titleService
|
||||||
* @param translate
|
* @param translate
|
||||||
* @param repo the repository
|
* @param matSnackBar
|
||||||
|
* @param repo the tag repository
|
||||||
|
* @param promptService
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
titleService: Title,
|
titleService: Title,
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
private repo: TagRepositoryService,
|
private repo: TagRepositoryService,
|
||||||
private promptService: PromptService
|
private promptService: PromptService
|
||||||
) {
|
) {
|
||||||
super(titleService, translate);
|
super(titleService, translate, matSnackBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,26 +74,26 @@ export class TagListComponent extends ListViewBaseComponent<ViewTag> implements
|
|||||||
/**
|
/**
|
||||||
* Saves a newly created tag.
|
* Saves a newly created tag.
|
||||||
*/
|
*/
|
||||||
public async submitNewTag(): Promise<void> {
|
public submitNewTag(): void {
|
||||||
if (!this.tagForm.value || !this.tagForm.valid) {
|
if (!this.tagForm.value || !this.tagForm.valid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await this.repo.create(this.tagForm.value);
|
this.repo.create(this.tagForm.value).then(() => {
|
||||||
this.tagForm.reset();
|
this.tagForm.reset();
|
||||||
this.cancelEditing();
|
this.cancelEditing();
|
||||||
|
}, this.raiseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves an edited tag.
|
* Saves an edited tag.
|
||||||
*/
|
*/
|
||||||
public async submitEditedTag(): Promise<void> {
|
public submitEditedTag(): void {
|
||||||
if (!this.tagForm.value || !this.tagForm.valid) {
|
if (!this.tagForm.value || !this.tagForm.valid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const updateData = new Tag({ name: this.tagForm.value.name });
|
const updateData = new Tag({ name: this.tagForm.value.name });
|
||||||
|
|
||||||
await this.repo.update(updateData, this.selectedTag);
|
this.repo.update(updateData, this.selectedTag).then(() => this.cancelEditing(), this.raiseError);
|
||||||
this.cancelEditing();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -98,11 +102,13 @@ export class TagListComponent extends ListViewBaseComponent<ViewTag> implements
|
|||||||
public async deleteSelectedTag(): Promise<void> {
|
public async deleteSelectedTag(): Promise<void> {
|
||||||
const content = this.translate.instant('Delete') + ` ${this.selectedTag.name}?`;
|
const content = this.translate.instant('Delete') + ` ${this.selectedTag.name}?`;
|
||||||
if (await this.promptService.open(this.translate.instant('Are you sure?'), content)) {
|
if (await this.promptService.open(this.translate.instant('Are you sure?'), content)) {
|
||||||
await this.repo.delete(this.selectedTag);
|
this.repo.delete(this.selectedTag).then(() => this.cancelEditing(), this.raiseError);
|
||||||
this.cancelEditing();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Canceles the editing
|
||||||
|
*/
|
||||||
public cancelEditing(): void {
|
public cancelEditing(): void {
|
||||||
this.newTag = false;
|
this.newTag = false;
|
||||||
this.editTag = false;
|
this.editTag = false;
|
||||||
@ -126,6 +132,11 @@ export class TagListComponent extends ListViewBaseComponent<ViewTag> implements
|
|||||||
this.cancelEditing();
|
this.cancelEditing();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles keyboard events. On enter, the editing is canceled.
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
public keyDownFunction(event: KeyboardEvent): void {
|
public keyDownFunction(event: KeyboardEvent): void {
|
||||||
if (event.keyCode === 27) {
|
if (event.keyCode === 27) {
|
||||||
this.cancelEditing();
|
this.cancelEditing();
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { MatTableDataSource } from '@angular/material';
|
import { MatTableDataSource, MatSnackBar } from '@angular/material';
|
||||||
import { FormGroup, FormControl, Validators } from '@angular/forms';
|
import { FormGroup, FormControl, Validators } from '@angular/forms';
|
||||||
|
|
||||||
import { GroupRepositoryService } from '../../services/group-repository.service';
|
import { GroupRepositoryService } from '../../services/group-repository.service';
|
||||||
import { ViewGroup } from '../../models/view-group';
|
import { ViewGroup } from '../../models/view-group';
|
||||||
import { Group } from '../../../../shared/models/users/group';
|
import { Group } from '../../../../shared/models/users/group';
|
||||||
import { BaseComponent } from '../../../../base.component';
|
import { BaseViewComponent } from '../../../base/base-view';
|
||||||
|
import { PromptService } from '../../../../core/services/prompt.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for the Group-List and permission matrix
|
* Component for the Group-List and permission matrix
|
||||||
@ -17,7 +18,7 @@ import { BaseComponent } from '../../../../base.component';
|
|||||||
templateUrl: './group-list.component.html',
|
templateUrl: './group-list.component.html',
|
||||||
styleUrls: ['./group-list.component.scss']
|
styleUrls: ['./group-list.component.scss']
|
||||||
})
|
})
|
||||||
export class GroupListComponent extends BaseComponent implements OnInit {
|
export class GroupListComponent extends BaseViewComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* Holds all Groups
|
* Holds all Groups
|
||||||
*/
|
*/
|
||||||
@ -51,22 +52,37 @@ export class GroupListComponent extends BaseComponent implements OnInit {
|
|||||||
*
|
*
|
||||||
* @param titleService Title Service
|
* @param titleService Title Service
|
||||||
* @param translate Translations
|
* @param translate Translations
|
||||||
* @param DS The Data Store
|
* @param matSnackBar
|
||||||
* @param constants Constants
|
* @param repo
|
||||||
|
* @param promptService
|
||||||
*/
|
*/
|
||||||
public constructor(titleService: Title, translate: TranslateService, public repo: GroupRepositoryService) {
|
public constructor(
|
||||||
super(titleService, translate);
|
titleService: Title,
|
||||||
|
translate: TranslateService,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
|
public repo: GroupRepositoryService,
|
||||||
|
private promptService: PromptService
|
||||||
|
) {
|
||||||
|
super(titleService, translate, matSnackBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setEditMode(mode: boolean, newGroup: boolean = true): void {
|
/**
|
||||||
this.editGroup = mode;
|
* Set, if the view is in edit mode. If editMod eis false, the editing is canceled.
|
||||||
|
* @param editMode
|
||||||
|
* @param newGroup Set to true, if the edit mode is for creating instead of updating a group.
|
||||||
|
*/
|
||||||
|
public setEditMode(editMode: boolean, newGroup: boolean = true): void {
|
||||||
|
this.editGroup = editMode;
|
||||||
this.newGroup = newGroup;
|
this.newGroup = newGroup;
|
||||||
|
|
||||||
if (!mode) {
|
if (!editMode) {
|
||||||
this.cancelEditing();
|
this.cancelEditing();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates or updates a group.
|
||||||
|
*/
|
||||||
public saveGroup(): void {
|
public saveGroup(): void {
|
||||||
if (this.editGroup && this.newGroup) {
|
if (this.editGroup && this.newGroup) {
|
||||||
this.submitNewGroup();
|
this.submitNewGroup();
|
||||||
@ -86,37 +102,39 @@ export class GroupListComponent extends BaseComponent implements OnInit {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves a newly created group.
|
* Saves a newly created group.
|
||||||
* @param form form data given by the group
|
|
||||||
*/
|
*/
|
||||||
public async submitNewGroup(): Promise<void> {
|
public submitNewGroup(): void {
|
||||||
if (!this.groupForm.value || !this.groupForm.valid) {
|
if (!this.groupForm.value || !this.groupForm.valid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await this.repo.create(this.groupForm.value);
|
this.repo.create(this.groupForm.value).then(() => {
|
||||||
this.groupForm.reset();
|
this.groupForm.reset();
|
||||||
this.cancelEditing();
|
this.cancelEditing();
|
||||||
|
}, this.raiseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves an edited group.
|
* Saves an edited group.
|
||||||
* @param form form data given by the group
|
|
||||||
*/
|
*/
|
||||||
public async submitEditedGroup(): Promise<void> {
|
public submitEditedGroup(): void {
|
||||||
if (!this.groupForm.value || !this.groupForm.valid) {
|
if (!this.groupForm.value || !this.groupForm.valid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const updateData = new Group({ name: this.groupForm.value.name });
|
const updateData = new Group({ name: this.groupForm.value.name });
|
||||||
|
|
||||||
await this.repo.update(updateData, this.selectedGroup);
|
this.repo.update(updateData, this.selectedGroup).then(() => {
|
||||||
this.cancelEditing();
|
this.cancelEditing();
|
||||||
|
}, this.raiseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the selected Group
|
* Deletes the selected Group
|
||||||
*/
|
*/
|
||||||
public async deleteSelectedGroup(): Promise<void> {
|
public async deleteSelectedGroup(): Promise<void> {
|
||||||
await this.repo.delete(this.selectedGroup)
|
const content = this.translate.instant('Delete') + ` ${this.selectedGroup.name}?`;
|
||||||
this.cancelEditing();
|
if (await this.promptService.open(this.translate.instant('Are you sure?'), content)) {
|
||||||
|
this.repo.delete(this.selectedGroup).then(() => this.cancelEditing(), this.raiseError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,12 +148,12 @@ export class GroupListComponent extends BaseComponent implements OnInit {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers when a permission was toggled
|
* Triggers when a permission was toggled
|
||||||
* @param group
|
* @param viewGroup
|
||||||
* @param perm
|
* @param perm
|
||||||
*/
|
*/
|
||||||
public async togglePerm(viewGroup: ViewGroup, perm: string): Promise<void> {
|
public togglePerm(viewGroup: ViewGroup, perm: string): void {
|
||||||
const updateData = new Group({ permissions: viewGroup.getAlteredPermissions(perm) });
|
const updateData = new Group({ permissions: viewGroup.getAlteredPermissions(perm) });
|
||||||
await this.repo.update(updateData, viewGroup);
|
this.repo.update(updateData, viewGroup).then(null, this.raiseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,6 +7,11 @@ import { UserRepositoryService } from '../../services/user-repository.service';
|
|||||||
import { Group } from '../../../../shared/models/users/group';
|
import { Group } from '../../../../shared/models/users/group';
|
||||||
import { DataStoreService } from '../../../../core/services/data-store.service';
|
import { DataStoreService } from '../../../../core/services/data-store.service';
|
||||||
import { OperatorService } from '../../../../core/services/operator.service';
|
import { OperatorService } from '../../../../core/services/operator.service';
|
||||||
|
import { BaseViewComponent } from '../../../base/base-view';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { MatSnackBar } from '@angular/material';
|
||||||
|
import { Title } from '@angular/platform-browser';
|
||||||
|
import { PromptService } from '../../../../core/services/prompt.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Users detail component for both new and existing users
|
* Users detail component for both new and existing users
|
||||||
@ -16,7 +21,7 @@ import { OperatorService } from '../../../../core/services/operator.service';
|
|||||||
templateUrl: './user-detail.component.html',
|
templateUrl: './user-detail.component.html',
|
||||||
styleUrls: ['./user-detail.component.scss']
|
styleUrls: ['./user-detail.component.scss']
|
||||||
})
|
})
|
||||||
export class UserDetailComponent implements OnInit {
|
export class UserDetailComponent extends BaseViewComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* Info form object
|
* Info form object
|
||||||
*/
|
*/
|
||||||
@ -54,15 +59,32 @@ export class UserDetailComponent implements OnInit {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for user
|
* Constructor for user
|
||||||
|
*
|
||||||
|
* @param title Title
|
||||||
|
* @param translate TranslateService
|
||||||
|
* @param matSnackBar MatSnackBar
|
||||||
|
* @param formBuilder FormBuilder
|
||||||
|
* @param route ActivatedRoute
|
||||||
|
* @param router Router
|
||||||
|
* @param repo UserRepositoryService
|
||||||
|
* @param DS DataStoreService
|
||||||
|
* @param operator OperatorService
|
||||||
|
* @param promptService PromptService
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
|
title: Title,
|
||||||
|
translate: TranslateService,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private repo: UserRepositoryService,
|
private repo: UserRepositoryService,
|
||||||
private DS: DataStoreService,
|
private DS: DataStoreService,
|
||||||
private op: OperatorService
|
private operator: OperatorService,
|
||||||
|
private promptService: PromptService
|
||||||
) {
|
) {
|
||||||
|
super(title, translate, matSnackBar);
|
||||||
|
|
||||||
this.user = new ViewUser();
|
this.user = new ViewUser();
|
||||||
if (route.snapshot.url[0] && route.snapshot.url[0].path === 'new') {
|
if (route.snapshot.url[0] && route.snapshot.url[0].path === 'new') {
|
||||||
this.newUser = true;
|
this.newUser = true;
|
||||||
@ -75,7 +97,7 @@ export class UserDetailComponent implements OnInit {
|
|||||||
this.ownPage = this.opOwnsPage(Number(params.id));
|
this.ownPage = this.opOwnsPage(Number(params.id));
|
||||||
|
|
||||||
// observe operator to find out if we see our own page or not
|
// observe operator to find out if we see our own page or not
|
||||||
this.op.getObservable().subscribe(newOp => {
|
this.operator.getObservable().subscribe(newOp => {
|
||||||
if (newOp) {
|
if (newOp) {
|
||||||
this.ownPage = this.opOwnsPage(Number(params.id));
|
this.ownPage = this.opOwnsPage(Number(params.id));
|
||||||
}
|
}
|
||||||
@ -86,10 +108,12 @@ export class UserDetailComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sets the ownPage variable if the operator owns the page
|
* Checks, if the given user id matches with the operator ones.
|
||||||
|
* @param userId The id to check, if it's the operator
|
||||||
|
* @returns If the user is the operator
|
||||||
*/
|
*/
|
||||||
public opOwnsPage(userId: number): boolean {
|
public opOwnsPage(userId: number): boolean {
|
||||||
return this.op.user && this.op.user.id === userId;
|
return this.operator.user && this.operator.user.id === userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -108,15 +132,15 @@ export class UserDetailComponent implements OnInit {
|
|||||||
public isAllowed(action: string): boolean {
|
public isAllowed(action: string): boolean {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'manage':
|
case 'manage':
|
||||||
return this.op.hasPerms('users.can_manage');
|
return this.operator.hasPerms('users.can_manage');
|
||||||
case 'seeName':
|
case 'seeName':
|
||||||
return this.op.hasPerms('users.can_see_name', 'users.can_manage') || this.ownPage;
|
return this.operator.hasPerms('users.can_see_name', 'users.can_manage') || this.ownPage;
|
||||||
case 'seeExtra':
|
case 'seeExtra':
|
||||||
return this.op.hasPerms('users.can_see_extra_data', 'users.can_manage');
|
return this.operator.hasPerms('users.can_see_extra_data', 'users.can_manage');
|
||||||
case 'seePersonal':
|
case 'seePersonal':
|
||||||
return this.op.hasPerms('users.can_see_extra_data', 'users.can_manage') || this.ownPage;
|
return this.operator.hasPerms('users.can_see_extra_data', 'users.can_manage') || this.ownPage;
|
||||||
case 'changePersonal':
|
case 'changePersonal':
|
||||||
return this.op.hasPerms('user.cans_manage') || this.ownPage;
|
return this.operator.hasPerms('user.cans_manage') || this.ownPage;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -254,6 +278,7 @@ export class UserDetailComponent implements OnInit {
|
|||||||
* Save / Submit a user
|
* Save / Submit a user
|
||||||
*/
|
*/
|
||||||
public async saveUser(): Promise<void> {
|
public async saveUser(): Promise<void> {
|
||||||
|
try {
|
||||||
if (this.newUser) {
|
if (this.newUser) {
|
||||||
const response = await this.repo.create(this.personalInfoForm.value);
|
const response = await this.repo.create(this.personalInfoForm.value);
|
||||||
this.newUser = false;
|
this.newUser = false;
|
||||||
@ -265,6 +290,9 @@ export class UserDetailComponent implements OnInit {
|
|||||||
this.setEditMode(false);
|
this.setEditMode(false);
|
||||||
this.loadViewUser(this.user.id);
|
this.loadViewUser(this.user.id);
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.raiseError(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -285,8 +313,10 @@ export class UserDetailComponent implements OnInit {
|
|||||||
* click on the delete user button
|
* click on the delete user button
|
||||||
*/
|
*/
|
||||||
public async deleteUserButton(): Promise<void> {
|
public async deleteUserButton(): Promise<void> {
|
||||||
await this.repo.delete(this.user);
|
const content = this.translate.instant('Delete') + ` ${this.user.full_name}?`;
|
||||||
this.router.navigate(['./users/']);
|
if (await this.promptService.open(this.translate.instant('Are you sure?'), content)) {
|
||||||
|
this.repo.delete(this.user).then(() => this.router.navigate(['./users/']), this.raiseError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,6 +7,7 @@ import { ViewUser } from '../../models/view-user';
|
|||||||
import { UserRepositoryService } from '../../services/user-repository.service';
|
import { UserRepositoryService } from '../../services/user-repository.service';
|
||||||
import { ListViewBaseComponent } from '../../../base/list-view-base';
|
import { ListViewBaseComponent } from '../../../base/list-view-base';
|
||||||
import { Router, ActivatedRoute } from '@angular/router';
|
import { Router, ActivatedRoute } from '@angular/router';
|
||||||
|
import { MatSnackBar } from '@angular/material';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for the user list view.
|
* Component for the user list view.
|
||||||
@ -26,14 +27,15 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
|
|||||||
* @param translate
|
* @param translate
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
|
titleService: Title,
|
||||||
|
translate: TranslateService,
|
||||||
|
matSnackBar: MatSnackBar,
|
||||||
private repo: UserRepositoryService,
|
private repo: UserRepositoryService,
|
||||||
protected titleService: Title,
|
|
||||||
protected translate: TranslateService,
|
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
protected csvExport: CsvExportService
|
protected csvExport: CsvExportService
|
||||||
) {
|
) {
|
||||||
super(titleService, translate);
|
super(titleService, translate, matSnackBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user