Enhance amendment wizard
- close button instead of back-button - "are you sure" prompt if chances to the wizard were made - edit and save events, like every other view - enhanced next, previous, create logic that follows validation also: - fixed a bug with custom cancel events in the head-bar - mobile button has the correct icon again
This commit is contained in:
parent
b1c02133ee
commit
af8b49450b
@ -15,20 +15,28 @@ export class RoutingStateService {
|
|||||||
/**
|
/**
|
||||||
* Hold the previous URL
|
* Hold the previous URL
|
||||||
*/
|
*/
|
||||||
private previousUrl: string;
|
private _previousUrl: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unsafe paths that the user should not go "back" to
|
* Unsafe paths that the user should not go "back" to
|
||||||
* TODO: Might also work using Routing parameters
|
* TODO: Might also work using Routing parameters
|
||||||
*/
|
*/
|
||||||
private unsafeUrls: string[] = ['/login', '/privacypolicy', '/legalnotice'];
|
private unsafeUrls: string[] = ['/login', '/privacypolicy', '/legalnotice', '/new', '/create'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the previous URL is safe to navigate to.
|
* Checks if the previous URL is safe to navigate to.
|
||||||
* If this fails, the open nav button should be shown
|
* If this fails, the open nav button should be shown
|
||||||
*/
|
*/
|
||||||
public get isSafePrevUrl(): boolean {
|
public get isSafePrevUrl(): boolean {
|
||||||
return !this.previousUrl || !this.unsafeUrls.includes(this.previousUrl);
|
if (this._previousUrl) {
|
||||||
|
return !this.unsafeUrls.some(unsafeUrl => this._previousUrl.includes(unsafeUrl));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get previousUrl(): string {
|
||||||
|
return this._previousUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,7 +51,7 @@ export class RoutingStateService {
|
|||||||
pairwise()
|
pairwise()
|
||||||
)
|
)
|
||||||
.subscribe((event: any[]) => {
|
.subscribe((event: any[]) => {
|
||||||
this.previousUrl = event[0].urlAfterRedirects;
|
this._previousUrl = event[0].urlAfterRedirects;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Cancel edit button -->
|
<!-- Cancel edit button -->
|
||||||
<button mat-icon-button *ngIf="editMode" (click)="cancelEditEvent ? sendCancelEditEvent() : sendMainEvent()">
|
<button mat-icon-button *ngIf="editMode" (click)="isCancelEditUsed ? sendCancelEditEvent() : sendMainEvent()">
|
||||||
<mat-icon>close</mat-icon>
|
<mat-icon>close</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@ -44,7 +44,7 @@
|
|||||||
|
|
||||||
<!-- Save button -->
|
<!-- Save button -->
|
||||||
<button mat-button *ngIf="editMode" [disabled]="!isSaveButtonEnabled" (click)="save()">
|
<button mat-button *ngIf="editMode" [disabled]="!isSaveButtonEnabled" (click)="save()">
|
||||||
<strong translate class="upper">Save</strong>
|
<strong translate class="upper">{{ saveText }}</strong>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Menu button slot -->
|
<!-- Menu button slot -->
|
||||||
@ -61,5 +61,10 @@
|
|||||||
(click)="sendMainEvent()"
|
(click)="sendMainEvent()"
|
||||||
matTooltip="{{ mainActionTooltip | translate }}"
|
matTooltip="{{ mainActionTooltip | translate }}"
|
||||||
>
|
>
|
||||||
<mat-icon>{{ mainButtonIcon }}</mat-icon>
|
<mat-icon *ngIf="mainButtonIcon === 'add_circle'; else mainIconBlock">
|
||||||
|
add
|
||||||
|
</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
<ng-template #mainIconBlock>
|
||||||
|
{{ mainButtonIcon }}
|
||||||
|
</ng-template>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
|
||||||
import { MainMenuService } from 'app/core/core-services/main-menu.service';
|
import { MainMenuService } from 'app/core/core-services/main-menu.service';
|
||||||
@ -17,11 +17,14 @@ import { ViewportService } from 'app/core/ui-services/viewport.service';
|
|||||||
* ```html
|
* ```html
|
||||||
* <os-head-bar
|
* <os-head-bar
|
||||||
* prevUrl="../.."
|
* prevUrl="../.."
|
||||||
|
* saveText="Create"
|
||||||
* [nav]="false"
|
* [nav]="false"
|
||||||
* [goBack]="true"
|
* [goBack]="true"
|
||||||
* [mainButton]="opCanEdit()"
|
* [mainButton]="opCanEdit()"
|
||||||
* [mainButtonIcon]="edit"
|
* [mainButtonIcon]="edit"
|
||||||
|
* [backButtonIcon]="arrow_back"
|
||||||
* [editMode]="editMotion"
|
* [editMode]="editMotion"
|
||||||
|
* [isSaveButtonEnabled]="myConditionIsTrue()"
|
||||||
* [multiSelectMode]="isMultiSelect"
|
* [multiSelectMode]="isMultiSelect"
|
||||||
* (mainEvent)="setEditMode(!editMotion)"
|
* (mainEvent)="setEditMode(!editMotion)"
|
||||||
* (saveEvent)="saveMotion()">
|
* (saveEvent)="saveMotion()">
|
||||||
@ -52,7 +55,7 @@ import { ViewportService } from 'app/core/ui-services/viewport.service';
|
|||||||
templateUrl: './head-bar.component.html',
|
templateUrl: './head-bar.component.html',
|
||||||
styleUrls: ['./head-bar.component.scss']
|
styleUrls: ['./head-bar.component.scss']
|
||||||
})
|
})
|
||||||
export class HeadBarComponent {
|
export class HeadBarComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* Determine if the the navigation "hamburger" icon should be displayed in mobile mode
|
* Determine if the the navigation "hamburger" icon should be displayed in mobile mode
|
||||||
*/
|
*/
|
||||||
@ -65,6 +68,12 @@ export class HeadBarComponent {
|
|||||||
@Input()
|
@Input()
|
||||||
public mainButtonIcon = 'add_circle';
|
public mainButtonIcon = 'add_circle';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom text to show as "save"
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
public saveText = 'Save';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine edit mode
|
* Determine edit mode
|
||||||
*/
|
*/
|
||||||
@ -123,6 +132,11 @@ export class HeadBarComponent {
|
|||||||
@Output()
|
@Output()
|
||||||
public cancelEditEvent = new EventEmitter<void>();
|
public cancelEditEvent = new EventEmitter<void>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To detect if the cancel event was used
|
||||||
|
*/
|
||||||
|
public isCancelEditUsed = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a signal if a detail view should be saved
|
* Sends a signal if a detail view should be saved
|
||||||
*/
|
*/
|
||||||
@ -144,6 +158,13 @@ export class HeadBarComponent {
|
|||||||
private routingState: RoutingStateService
|
private routingState: RoutingStateService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect if the cancel edit event was used
|
||||||
|
*/
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.isCancelEditUsed = this.cancelEditEvent.observers.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emits a signal to the parent if
|
* Emits a signal to the parent if
|
||||||
*/
|
*/
|
||||||
|
@ -1,15 +1,24 @@
|
|||||||
<os-head-bar [nav]="false" [goBack]="false">
|
<os-head-bar
|
||||||
|
[nav]="false"
|
||||||
|
[editMode]="true"
|
||||||
|
saveText="Create"
|
||||||
|
[isSaveButtonEnabled]="matStepper.selectedIndex === 1"
|
||||||
|
(saveEvent)="saveAmendment()"
|
||||||
|
(cancelEditEvent)="cancelCreation()"
|
||||||
|
>
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<div class="title-slot"><h2 translate>New amendment</h2></div>
|
<div class="title-slot"><h2 translate>New amendment</h2></div>
|
||||||
<div class="menu-slot">
|
|
||||||
|
<!-- Next-button -->
|
||||||
|
<div class="extra-controls-slot">
|
||||||
<div *ngIf="matStepper.selectedIndex === 0">
|
<div *ngIf="matStepper.selectedIndex === 0">
|
||||||
<button mat-button [disabled]="contentForm.value.selectedParagraph === null" (click)="matStepper.next()">
|
<button mat-button [disabled]="contentForm.value.selectedParagraphs.length === 0" (click)="matStepper.next()">
|
||||||
<span class="upper" translate>Next</span>
|
<span class="upper" translate>Next</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="matStepper.selectedIndex === 1">
|
<div *ngIf="matStepper.selectedIndex === 1">
|
||||||
<button type="button" mat-button (click)="saveAmendment()">
|
<button type="button" mat-button (click)="matStepper.previous()">
|
||||||
<span class="upper" translate>Create</span>
|
<span class="upper" translate>Previous</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,6 +8,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
|
|
||||||
import { MotionRepositoryService, ParagraphToChoose } from 'app/core/repositories/motions/motion-repository.service';
|
import { MotionRepositoryService, ParagraphToChoose } from 'app/core/repositories/motions/motion-repository.service';
|
||||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
import { CreateMotion } from 'app/site/motions/models/create-motion';
|
import { CreateMotion } from 'app/site/motions/models/create-motion';
|
||||||
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
||||||
@ -54,14 +55,15 @@ export class AmendmentCreateWizardComponent extends BaseViewComponent {
|
|||||||
/**
|
/**
|
||||||
* Constructs this component.
|
* Constructs this component.
|
||||||
*
|
*
|
||||||
* @param {Title} titleService set the browser title
|
* @param titleService set the browser title
|
||||||
* @param {TranslateService} translate the translation service
|
* @param translate the translation service
|
||||||
* @param {ConfigService} configService The configuration provider
|
* @param configService The configuration provider
|
||||||
* @param {FormBuilder} formBuilder Form builder
|
* @param formBuilder Form builder
|
||||||
* @param {MotionRepositoryService} repo Motion Repository
|
* @param repo Motion Repository
|
||||||
* @param {ActivatedRoute} route The activated route
|
* @param route The activated route
|
||||||
* @param {Router} router The router
|
* @param router The router
|
||||||
* @param {MatSnackBar} matSnackBar Material Design SnackBar
|
* @param promptService Show a prompt by leaving the view
|
||||||
|
* @param matSnackBar Material Design SnackBar
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
titleService: Title,
|
titleService: Title,
|
||||||
@ -71,6 +73,7 @@ export class AmendmentCreateWizardComponent extends BaseViewComponent {
|
|||||||
private repo: MotionRepositoryService,
|
private repo: MotionRepositoryService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
|
private promptService: PromptService,
|
||||||
matSnackBar: MatSnackBar
|
matSnackBar: MatSnackBar
|
||||||
) {
|
) {
|
||||||
super(titleService, translate, matSnackBar);
|
super(titleService, translate, matSnackBar);
|
||||||
@ -103,6 +106,21 @@ export class AmendmentCreateWizardComponent extends BaseViewComponent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the editing.
|
||||||
|
* Only fires when the form was dirty
|
||||||
|
*/
|
||||||
|
public async cancelCreation(): Promise<void> {
|
||||||
|
if (this.contentForm.dirty || this.contentForm.value.selectedParagraphs.length > 0) {
|
||||||
|
const title = this.translate.instant('Are you sure you want to discard this amendment?');
|
||||||
|
if (await this.promptService.open(title)) {
|
||||||
|
this.router.navigate(['..'], { relativeTo: this.route });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.router.navigate(['..'], { relativeTo: this.route });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the forms for the Motion and the MotionVersion
|
* Creates the forms for the Motion and the MotionVersion
|
||||||
*/
|
*/
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<os-head-bar
|
<os-head-bar
|
||||||
[mainButton]="perms.isAllowed('can_create_amendments', motion)"
|
[mainButton]="perms.isAllowed('can_create_amendments', motion)"
|
||||||
mainActionTooltip="New amendment"
|
mainActionTooltip="New amendment"
|
||||||
prevUrl="../.."
|
[prevUrl]="getPrevUrl()"
|
||||||
[goBack]="motion && !!motion.parent_id"
|
[goBack]="routingStateService.isSafePrevUrl"
|
||||||
[nav]="false"
|
[nav]="false"
|
||||||
[editMode]="editMotion"
|
[editMode]="editMotion"
|
||||||
[isSaveButtonEnabled]="contentForm.valid"
|
[isSaveButtonEnabled]="contentForm.valid"
|
||||||
|
@ -26,6 +26,7 @@ import { DiffLinesInParagraph, DiffService, LineRange } from 'app/core/ui-servic
|
|||||||
import { LinenumberingService } from 'app/core/ui-services/linenumbering.service';
|
import { LinenumberingService } from 'app/core/ui-services/linenumbering.service';
|
||||||
import { PersonalNoteService } from 'app/core/ui-services/personal-note.service';
|
import { PersonalNoteService } from 'app/core/ui-services/personal-note.service';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
|
import { RoutingStateService } from 'app/core/ui-services/routing-state.service';
|
||||||
import { ViewportService } from 'app/core/ui-services/viewport.service';
|
import { ViewportService } from 'app/core/ui-services/viewport.service';
|
||||||
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
||||||
import { Motion } from 'app/shared/models/motions/motion';
|
import { Motion } from 'app/shared/models/motions/motion';
|
||||||
@ -445,7 +446,8 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
|||||||
private blockRepo: MotionBlockRepositoryService,
|
private blockRepo: MotionBlockRepositoryService,
|
||||||
private itemRepo: ItemRepositoryService,
|
private itemRepo: ItemRepositoryService,
|
||||||
private motionSortService: MotionSortListService,
|
private motionSortService: MotionSortListService,
|
||||||
private motionFilterService: MotionFilterListService
|
private motionFilterService: MotionFilterListService,
|
||||||
|
public routingStateService: RoutingStateService
|
||||||
) {
|
) {
|
||||||
super(title, translate, matSnackBar);
|
super(title, translate, matSnackBar);
|
||||||
}
|
}
|
||||||
@ -1548,15 +1550,17 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to "logically" navigate back. If the motion has a parent, it will
|
* Tries to determine the previous URL if it's considered unsafe
|
||||||
* try to navigate to the parent
|
|
||||||
* rather than just into the list view.
|
|
||||||
*
|
*
|
||||||
* @returns the target to navigate to
|
* @returns the target to navigate to
|
||||||
*/
|
*/
|
||||||
public getPrevUrl(): string {
|
public getPrevUrl(): string {
|
||||||
if (this.motion && this.motion.parent_id) {
|
if (this.motion && this.motion.parent_id) {
|
||||||
return `../../${this.motion.parent_id}`;
|
if (this.routingStateService.previousUrl && this.routingStateService.isSafePrevUrl) {
|
||||||
|
return this.routingStateService.previousUrl;
|
||||||
|
} else {
|
||||||
|
return this.motion.parent.getDetailStateURL();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return '../..';
|
return '../..';
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,7 @@
|
|||||||
|
|
||||||
<mat-menu #motionListMenu="matMenu">
|
<mat-menu #motionListMenu="matMenu">
|
||||||
<div *ngIf="!isMultiSelect">
|
<div *ngIf="!isMultiSelect">
|
||||||
<div *ngIf="perms.isAllowed('change_metadata')">
|
<div *ngIf="perms.isAllowed('change_metadata') && selectedView === 'list'">
|
||||||
<button mat-menu-item (click)="toggleMultiSelect()">
|
<button mat-menu-item (click)="toggleMultiSelect()">
|
||||||
<mat-icon>library_add</mat-icon>
|
<mat-icon>library_add</mat-icon>
|
||||||
<span translate>Multiselect</span>
|
<span translate>Multiselect</span>
|
||||||
@ -265,7 +265,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button mat-menu-item (click)="openExportDialog()">
|
<button mat-menu-item *ngIf="selectedView === 'list'" (click)="openExportDialog()">
|
||||||
<mat-icon>archive</mat-icon>
|
<mat-icon>archive</mat-icon>
|
||||||
<span translate>Export</span>
|
<span translate>Export</span>
|
||||||
</button>
|
</button>
|
||||||
|
@ -31,6 +31,14 @@ import { MotionSortListService } from 'app/site/motions/services/motion-sort-lis
|
|||||||
import { ViewTag } from 'app/site/tags/models/view-tag';
|
import { ViewTag } from 'app/site/tags/models/view-tag';
|
||||||
import { MotionExportDialogComponent } from '../../../shared-motion/motion-export-dialog/motion-export-dialog.component';
|
import { MotionExportDialogComponent } from '../../../shared-motion/motion-export-dialog/motion-export-dialog.component';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the types of the motionList
|
||||||
|
*/
|
||||||
|
type MotionListviewType = 'tiles' | 'list';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tile information
|
||||||
|
*/
|
||||||
interface TileCategoryInformation {
|
interface TileCategoryInformation {
|
||||||
filter: string;
|
filter: string;
|
||||||
name: string;
|
name: string;
|
||||||
@ -88,7 +96,7 @@ export class MotionListComponent extends BaseListViewComponent<ViewMotion> imple
|
|||||||
/**
|
/**
|
||||||
* String to define the current selected view.
|
* String to define the current selected view.
|
||||||
*/
|
*/
|
||||||
public selectedView: string;
|
public selectedView: MotionListviewType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Columns to display in table when desktop view is available
|
* Columns to display in table when desktop view is available
|
||||||
@ -236,7 +244,7 @@ export class MotionListComponent extends BaseListViewComponent<ViewMotion> imple
|
|||||||
this.categoryRepo.getViewModelListObservable().subscribe(cats => {
|
this.categoryRepo.getViewModelListObservable().subscribe(cats => {
|
||||||
this.categories = cats;
|
this.categories = cats;
|
||||||
if (cats.length > 0) {
|
if (cats.length > 0) {
|
||||||
this.storage.get<string>('motionListView').then(savedView => {
|
this.storage.get<string>('motionListView').then((savedView: MotionListviewType) => {
|
||||||
this.selectedView = savedView ? savedView : 'tiles';
|
this.selectedView = savedView ? savedView : 'tiles';
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -381,7 +389,7 @@ export class MotionListComponent extends BaseListViewComponent<ViewMotion> imple
|
|||||||
*
|
*
|
||||||
* @param value is the new view the user has selected.
|
* @param value is the new view the user has selected.
|
||||||
*/
|
*/
|
||||||
public onChangeView(value: string): void {
|
public onChangeView(value: MotionListviewType): void {
|
||||||
this.selectedView = value;
|
this.selectedView = value;
|
||||||
this.storage.set('motionListView', value);
|
this.storage.set('motionListView', value);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user