Merge pull request #4339 from MaximilianKrambach/finalVersion
editor for final modified version of a motion
This commit is contained in:
commit
441e10ed59
@ -42,6 +42,14 @@ export enum ChangeRecoMode {
|
||||
ModifiedFinal = 'modified_final_version'
|
||||
}
|
||||
|
||||
export const verboseChangeRecoMode = {
|
||||
original: 'Original version',
|
||||
changed: 'Changed version',
|
||||
diff: 'Diff version',
|
||||
agreed: 'Final version',
|
||||
modified_final_version: 'Final print template'
|
||||
};
|
||||
|
||||
export interface MotionTitleInformation extends TitleInformationWithAgendaItem {
|
||||
title: string;
|
||||
identifier?: string;
|
||||
|
@ -38,6 +38,8 @@
|
||||
<div *ngIf="changes.length === 0" class="no-changes">{{ 'No change recommendations yet' | translate }}</div>
|
||||
</section>
|
||||
|
||||
<span class="text-prefix-label">{{ preamble | translate }}</span>
|
||||
|
||||
<!-- The actual diff view -->
|
||||
<div class="motion-text-with-diffs">
|
||||
<div *ngFor="let change of changes; let i = index">
|
||||
|
@ -58,7 +58,7 @@
|
||||
background-color: #eee;
|
||||
border: solid 1px #ddd;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 5px;
|
||||
margin-bottom: 15px;
|
||||
padding: 5px 10px;
|
||||
|
||||
a,
|
||||
|
@ -64,6 +64,8 @@ export class MotionDetailDiffComponent extends BaseViewComponent implements Afte
|
||||
*/
|
||||
public lineLength: number;
|
||||
|
||||
public preamble: string;
|
||||
|
||||
/**
|
||||
* @param title
|
||||
* @param translate
|
||||
@ -92,6 +94,7 @@ export class MotionDetailDiffComponent extends BaseViewComponent implements Afte
|
||||
) {
|
||||
super(title, translate, matSnackBar);
|
||||
this.configService.get<number>('motions_line_length').subscribe(lineLength => (this.lineLength = lineLength));
|
||||
this.configService.get<string>('motions_preamble').subscribe(preamble => (this.preamble = preamble));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -436,83 +436,115 @@
|
||||
|
||||
<ng-template #contentTemplate>
|
||||
<form class="motion-content" [formGroup]="contentForm" (keydown)="onKeyDown($event)">
|
||||
<!-- Line Number and Diff buttons -->
|
||||
<div *ngIf="!editMotion && !motion.isStatuteAmendment()" class="motion-text-controls">
|
||||
<mat-form-field class="motion-goto-line" *ngIf="highlightedLineOpened">
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
matInput
|
||||
placeholder="{{ 'Go to line' | translate }}"
|
||||
osAutofocus
|
||||
[(ngModel)]="highlightedLineTyping"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
[errorStateMatcher]="highlightedLineMatcher"
|
||||
/>
|
||||
<mat-error *ngIf="highlightedLineTyping > 10" translate>Invalid line number</mat-error>
|
||||
<button
|
||||
type="submit"
|
||||
mat-button
|
||||
matSuffix
|
||||
mat-icon-button
|
||||
aria-label="Go to line"
|
||||
*ngIf="highlightedLineTyping"
|
||||
(click)="gotoHighlightedLine(highlightedLineTyping); highlightedLineTyping = ''"
|
||||
>
|
||||
<mat-icon>redo</mat-icon>
|
||||
|
||||
<!-- Toolbar with text controls and buttonf for managing the (modified) final version-->
|
||||
<div class="motion-text-toolbar-wrapper outline-border-bottom">
|
||||
<!-- Line Number and Diff buttons -->
|
||||
<div *ngIf="!editMotion && !motion.isStatuteAmendment()" class="motion-text-controls">
|
||||
<mat-form-field class="motion-goto-line" *ngIf="highlightedLineOpened">
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
matInput
|
||||
placeholder="{{ 'Go to line' | translate }}"
|
||||
osAutofocus
|
||||
[(ngModel)]="highlightedLineTyping"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
[errorStateMatcher]="highlightedLineMatcher"
|
||||
/>
|
||||
<mat-error *ngIf="highlightedLineTyping > 10" translate>Invalid line number</mat-error>
|
||||
<button
|
||||
type="submit"
|
||||
mat-button
|
||||
matSuffix
|
||||
mat-icon-button
|
||||
aria-label="Go to line"
|
||||
*ngIf="highlightedLineTyping"
|
||||
(click)="gotoHighlightedLine(highlightedLineTyping); highlightedLineTyping = ''"
|
||||
>
|
||||
<mat-icon>redo</mat-icon>
|
||||
</button>
|
||||
</mat-form-field>
|
||||
<button mat-icon-button (click)="highlightedLineOpened = false" *ngIf="highlightedLineOpened">
|
||||
<mat-icon>cancel</mat-icon>
|
||||
</button>
|
||||
</mat-form-field>
|
||||
<button
|
||||
mat-icon-button
|
||||
matTooltip="{{ 'Go to line' | translate }}"
|
||||
*ngIf="!highlightedLineOpened"
|
||||
(click)="highlightedLineOpened = true"
|
||||
>
|
||||
<mat-icon>redo</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="highlightedLineOpened = false" *ngIf="highlightedLineOpened">
|
||||
<mat-icon>cancel</mat-icon>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="lineNumberingMenu"
|
||||
matTooltip="{{ 'Line numbering' | translate }}"
|
||||
>
|
||||
<mat-icon>format_list_numbered</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="changeRecoMenu"
|
||||
matTooltip="{{ 'Change recommendations' | translate }}"
|
||||
*ngIf="
|
||||
motion && !motion.isParagraphBasedAmendment() && allChangingObjects && allChangingObjects.length > 0
|
||||
"
|
||||
>
|
||||
<mat-icon>rate_review</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
mat-button
|
||||
[matMenuTriggerFor]="lineNumberingMenu"
|
||||
>
|
||||
<mat-icon>format_list_numbered</mat-icon>
|
||||
<span translate>Line numbering</span>
|
||||
<span *ngIf="lnMode === LineNumberingMode.None">
|
||||
(<span translate>none</span>)
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
mat-button
|
||||
[matMenuTriggerFor]="changeRecoMenu"
|
||||
*ngIf="
|
||||
motion && !motion.isParagraphBasedAmendment() && allChangingObjects && allChangingObjects.length > 0
|
||||
"
|
||||
>
|
||||
<mat-icon>rate_review</mat-icon>
|
||||
<span>{{ verboseChangeRecoMode[crMode] | translate }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
mat-icon-button
|
||||
matTooltip="{{ 'Create final print template' | translate }}"
|
||||
*osPerms="'motions.can_manage'; and: isRecoMode(ChangeRecoMode.Final)"
|
||||
(click)="createModifiedFinalVersion()"
|
||||
>
|
||||
<mat-icon>description</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="red-warning-text"
|
||||
mat-button
|
||||
matTooltip="{{ 'Delete final print template' | translate }}"
|
||||
*ngIf="isRecoMode(ChangeRecoMode.ModifiedFinal)"
|
||||
(click)="deleteModifiedFinalVersion()"
|
||||
>
|
||||
<mat-icon>description</mat-icon>
|
||||
<!-- Final edit buttons -->
|
||||
<div *ngIf="isRecoMode(ChangeRecoMode.ModifiedFinal) || isRecoMode(ChangeRecoMode.Final)">
|
||||
<!-- create final version -->
|
||||
<button
|
||||
type="button"
|
||||
mat-icon-button
|
||||
matTooltip="{{ 'Copy to final print template' | translate }}"
|
||||
*osPerms="'motions.can_manage'; and: isRecoMode(ChangeRecoMode.Final)"
|
||||
(click)="createModifiedFinalVersion()"
|
||||
>
|
||||
<mat-icon>file_copy</mat-icon>
|
||||
</button>
|
||||
<!-- edit final version -->
|
||||
<button
|
||||
type="button"
|
||||
mat-icon-button
|
||||
*ngIf="isRecoMode(ChangeRecoMode.ModifiedFinal) && !isFinalEdit"
|
||||
(click)="editModifiedFinal()"
|
||||
>
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<!-- save final version -->
|
||||
<button
|
||||
type="button"
|
||||
*ngIf="isRecoMode(ChangeRecoMode.ModifiedFinal) && isFinalEdit"
|
||||
mat-icon-button
|
||||
[disabled]="!finalVersionEdited"
|
||||
(click)="onSubmitFinalVersion()"
|
||||
>
|
||||
<mat-icon>save</mat-icon>
|
||||
</button>
|
||||
<!-- cancel final version edit -->
|
||||
<button
|
||||
type="button"
|
||||
*ngIf="isRecoMode(ChangeRecoMode.ModifiedFinal) && isFinalEdit"
|
||||
mat-icon-button
|
||||
(click)="cancelFinalVersionEdit()"
|
||||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<!-- delete final version edit -->
|
||||
<button
|
||||
type="button"
|
||||
class="red-warning-text"
|
||||
mat-icon-button
|
||||
matTooltip="{{ 'Delete final print template' | translate }}"
|
||||
*ngIf="isRecoMode(ChangeRecoMode.ModifiedFinal) && !isFinalEdit"
|
||||
(click)="deleteModifiedFinalVersion()"
|
||||
>
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Selecting statute paragraphs for amendment -->
|
||||
@ -579,13 +611,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Text -->
|
||||
<span class="text-prefix-label">{{ preamble | translate }}</span>
|
||||
<!-- Text (hide preamble, if diff mode. The preample is included in the motion-detail-diff component) -->
|
||||
<span *ngIf="!isRecoMode(ChangeRecoMode.Diff)" class="text-prefix-label">{{ preamble | translate }}</span>
|
||||
|
||||
<!-- Regular motions or traditional amendments -->
|
||||
<ng-container *ngIf="!editMotion && !motion.isStatuteAmendment() && !motion.isParagraphBasedAmendment()">
|
||||
<div
|
||||
*ngIf="!isRecoMode(ChangeRecoMode.Diff)"
|
||||
*ngIf="!isRecoMode(ChangeRecoMode.Diff) && !isFinalEdit"
|
||||
class="motion-text"
|
||||
[class.line-numbers-none]="isLineNumberingNone()"
|
||||
[class.line-numbers-inline]="isLineNumberingInline()"
|
||||
@ -612,7 +644,23 @@
|
||||
[lineNumberingMode]="lnMode"
|
||||
(createChangeRecommendation)="createChangeRecommendation($event)"
|
||||
></os-motion-detail-diff>
|
||||
|
||||
<div *ngIf="isFinalEdit">
|
||||
<editor [hidden]="!isFinalEdit" formControlName="modified_final_version" [init]="tinyMceSettings" required></editor>
|
||||
<div
|
||||
*ngIf="
|
||||
contentForm.get('modified_final_version').invalid &&
|
||||
(contentForm.get('modified_final_version').dirty || contentForm.get('modified_final_version').touched)
|
||||
"
|
||||
class="red-warning-text"
|
||||
translate
|
||||
>
|
||||
This field is required.
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<!-- formatted statute amendment -->
|
||||
<div
|
||||
class="motion-text line-numbers-none"
|
||||
*ngIf="!editMotion && motion.isStatuteAmendment()"
|
||||
@ -878,6 +926,15 @@
|
||||
>
|
||||
outside
|
||||
</button>
|
||||
<mat-divider></mat-divider>
|
||||
<button
|
||||
mat-menu-item
|
||||
*ngIf="!highlightedLineOpened"
|
||||
(click)="highlightedLineOpened = true"
|
||||
>
|
||||
<mat-icon>redo</mat-icon>
|
||||
<span translate>Go to line</span>
|
||||
</button>
|
||||
</div>
|
||||
</mat-menu>
|
||||
|
||||
@ -918,9 +975,9 @@
|
||||
<button
|
||||
mat-menu-item
|
||||
translate
|
||||
*osPerms="'motions.can_manage'; and: isRecoMode(ChangeRecoMode.Final)"
|
||||
*ngIf="motion && motion.modified_final_version"
|
||||
(click)="setChangeRecoMode(ChangeRecoMode.ModifiedFinal)"
|
||||
[ngClass]="{ selected: motion?.crMode === ChangeRecoMode.ModifiedFinal }"
|
||||
[ngClass]="{ selected: crMode === ChangeRecoMode.ModifiedFinal }"
|
||||
>
|
||||
Final print template
|
||||
</button>
|
||||
|
@ -23,30 +23,36 @@ span {
|
||||
display: flow-root;
|
||||
}
|
||||
|
||||
.motion-text-controls {
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
margin-top: -15px;
|
||||
height: 50px;
|
||||
.motion-text-toolbar-wrapper {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
button {
|
||||
font-size: 115%;
|
||||
font-weight: 400;
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
height: 50px;
|
||||
margin: -16px -16px 5px -16px;
|
||||
padding: 0 16px;
|
||||
|
||||
> button {
|
||||
// Prevent moving the buttons when the "go to line"-input is shown
|
||||
margin-top: 7px;
|
||||
}
|
||||
.motion-goto-line {
|
||||
width: 150px;
|
||||
}
|
||||
input[type='number']::-webkit-inner-spin-button,
|
||||
input[type='number']::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
input[type='number'] {
|
||||
-moz-appearance: textfield;
|
||||
|
||||
.motion-text-controls {
|
||||
.motion-goto-line {
|
||||
width: 150px;
|
||||
}
|
||||
input[type='number']::-webkit-inner-spin-button,
|
||||
input[type='number']::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
input[type='number'] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,12 @@ import {
|
||||
ViewMotionNotificationEditMotion,
|
||||
TypeOfNotificationViewMotion
|
||||
} from 'app/site/motions/models/view-motion-notify';
|
||||
import { ViewMotion, ChangeRecoMode, LineNumberingMode } from 'app/site/motions/models/view-motion';
|
||||
import {
|
||||
ViewMotion,
|
||||
ChangeRecoMode,
|
||||
LineNumberingMode,
|
||||
verboseChangeRecoMode
|
||||
} from 'app/site/motions/models/view-motion';
|
||||
import { ViewStatuteParagraph } from 'app/site/motions/models/view-statute-paragraph';
|
||||
import { ViewTag } from 'app/site/tags/models/view-tag';
|
||||
import { ViewUnifiedChange } from 'app/shared/models/motions/view-unified-change';
|
||||
@ -123,6 +128,30 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
||||
return this.repo.getExtendedStateLabel(this.motion);
|
||||
}
|
||||
|
||||
private finalEditMode = false;
|
||||
|
||||
/**
|
||||
* check if the 'final version edit mode' is active
|
||||
*
|
||||
* @returns true if active
|
||||
*/
|
||||
public get isFinalEdit(): boolean {
|
||||
return this.finalEditMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to check the current state of the final version edit
|
||||
*
|
||||
* @returns true if the local edit of the modified_final_version differs
|
||||
* from the submitted version
|
||||
*/
|
||||
public get finalVersionEdited(): boolean {
|
||||
return (
|
||||
this.crMode === ChangeRecoMode.ModifiedFinal &&
|
||||
this.contentForm.get('modified_final_version').value !== this.motion.modified_final_version
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the target motion. Accessed via the getter and setter.
|
||||
*/
|
||||
@ -275,6 +304,8 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
||||
*/
|
||||
public ChangeRecoMode = ChangeRecoMode;
|
||||
|
||||
public verboseChangeRecoMode = verboseChangeRecoMode;
|
||||
|
||||
/**
|
||||
* For using the enum constants from the template
|
||||
*/
|
||||
@ -704,8 +735,9 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
||||
selected_paragraphs: [],
|
||||
statute_amendment: [''], // Internal value for the checkbox, not saved to the model
|
||||
statute_paragraph_id: [''],
|
||||
motion_block_id: [], // TODO: Can be removed if this is not required
|
||||
parent_id: []
|
||||
motion_block_id: [],
|
||||
parent_id: [],
|
||||
modified_final_version: ['']
|
||||
});
|
||||
this.updateWorkflowIdForCreateForm();
|
||||
|
||||
@ -1062,7 +1094,10 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
||||
'Are you sure you want to copy the final version to the print template?'
|
||||
);
|
||||
if (await this.promptService.open(title, null)) {
|
||||
await this.updateMotion({ modified_final_version: finalVersion }, this.motion);
|
||||
this.updateMotion({ modified_final_version: finalVersion }, this.motion).then(
|
||||
() => this.setChangeRecoMode(ChangeRecoMode.ModifiedFinal),
|
||||
this.raiseError
|
||||
);
|
||||
}
|
||||
} else {
|
||||
await this.updateMotion({ modified_final_version: finalVersion }, this.motion);
|
||||
@ -1070,7 +1105,6 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
||||
} catch (e) {
|
||||
this.raiseError(e);
|
||||
}
|
||||
this.setChangeRecoMode(ChangeRecoMode.ModifiedFinal);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1079,6 +1113,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
||||
public async deleteModifiedFinalVersion(): Promise<void> {
|
||||
const title = this.translate.instant('Are you sure you want to delete the print template?');
|
||||
if (await this.promptService.open(title, null)) {
|
||||
this.finalEditMode = false;
|
||||
this.updateMotion({ modified_final_version: '' }, this.motion).then(
|
||||
() => this.setChangeRecoMode(ChangeRecoMode.Final),
|
||||
this.raiseError
|
||||
@ -1444,6 +1479,27 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits the modified final version of the motion
|
||||
*/
|
||||
public onSubmitFinalVersion(): void {
|
||||
const val = this.contentForm.get('modified_final_version').value;
|
||||
this.updateMotion({ modified_final_version: val }, this.motion).then(() => {
|
||||
this.finalEditMode = false;
|
||||
this.contentForm.get('modified_final_version').markAsPristine();
|
||||
}, this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the final version edit and resets the form value
|
||||
*
|
||||
* TODO: the tinyMCE editor content should reset, too
|
||||
*/
|
||||
public cancelFinalVersionEdit(): void {
|
||||
this.finalEditMode = false;
|
||||
this.contentForm.patchValue({ modified_final_version: this.motion.modified_final_version });
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the favorite status
|
||||
*/
|
||||
@ -1548,4 +1604,11 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the 'edit final version' mode
|
||||
*/
|
||||
public editModifiedFinal(): void {
|
||||
this.finalEditMode = true;
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,10 @@
|
||||
color: mat-color($warn);
|
||||
}
|
||||
|
||||
.outline-border-bottom {
|
||||
border-bottom: 1px solid $os-outline;
|
||||
}
|
||||
|
||||
/* motion list/detail view */
|
||||
.mat-chip-list.user .mat-chip {
|
||||
color: mat-color($foreground, text);
|
||||
|
Loading…
Reference in New Issue
Block a user