Pull request #60: Rules editor
Merge in RED/ui from rules to master * commit 'f1cd540de3e4698a80c612b050b8340892b2c0e3': Editor theme Rules editor
This commit is contained in:
commit
7bcb8c04e5
12
angular.json
12
angular.json
@ -33,10 +33,20 @@
|
|||||||
"input": "apps/red-ui/src/assets/",
|
"input": "apps/red-ui/src/assets/",
|
||||||
"output": "/assets/"
|
"output": "/assets/"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"glob": "**/*",
|
||||||
|
"input": "node_modules/ace-builds/src-min/",
|
||||||
|
"output": "/assets/ace-builds"
|
||||||
|
},
|
||||||
"apps/red-ui/src/manifest.webmanifest"
|
"apps/red-ui/src/manifest.webmanifest"
|
||||||
],
|
],
|
||||||
"styles": ["apps/red-ui/src/styles.scss"],
|
"styles": ["apps/red-ui/src/styles.scss"],
|
||||||
"scripts": ["node_modules/@pdftron/webviewer/webviewer.min.js", "node_modules/ace-builds/src-min/ace.js"]
|
"scripts": [
|
||||||
|
"node_modules/@pdftron/webviewer/webviewer.min.js",
|
||||||
|
"node_modules/ace-builds/src-min/ace.js",
|
||||||
|
"node_modules/ace-builds/src-min/mode-java.js",
|
||||||
|
"node_modules/ace-builds/src-min/theme-eclipse.js"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { AppLoadStateService } from './utils/app-load-state.service';
|
import { AppLoadStateService } from './utils/app-load-state.service';
|
||||||
|
|
||||||
|
declare var ace;
|
||||||
|
|
||||||
|
ace.config.set('basePath', '/assets/ace-builds/');
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-root',
|
selector: 'redaction-root',
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
|
|||||||
@ -92,6 +92,7 @@ import { TeamMembersComponent } from './components/team-members/team-members.com
|
|||||||
import { AdminBreadcrumbsComponent } from './components/admin-page-header/admin-breadcrumbs.component';
|
import { AdminBreadcrumbsComponent } from './components/admin-page-header/admin-breadcrumbs.component';
|
||||||
import { UserListingScreenComponent } from './screens/admin/users/user-listing-screen.component';
|
import { UserListingScreenComponent } from './screens/admin/users/user-listing-screen.component';
|
||||||
import { NotificationsComponent } from './components/notifications/notifications.component';
|
import { NotificationsComponent } from './components/notifications/notifications.component';
|
||||||
|
import { RulesScreenComponent } from './screens/admin/rules-screen/rules-screen.component';
|
||||||
|
|
||||||
export function HttpLoaderFactory(httpClient: HttpClient) {
|
export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||||
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
|
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
|
||||||
@ -167,6 +168,14 @@ const routes = [
|
|||||||
data: {
|
data: {
|
||||||
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
|
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'rules',
|
||||||
|
component: RulesScreenComponent,
|
||||||
|
canActivate: [CompositeRouteGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -248,7 +257,8 @@ const matImports = [
|
|||||||
TeamMembersComponent,
|
TeamMembersComponent,
|
||||||
AdminBreadcrumbsComponent,
|
AdminBreadcrumbsComponent,
|
||||||
UserListingScreenComponent,
|
UserListingScreenComponent,
|
||||||
NotificationsComponent
|
NotificationsComponent,
|
||||||
|
RulesScreenComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
|||||||
@ -8,6 +8,15 @@
|
|||||||
*ngIf="screen === 'dictionaries' || root"
|
*ngIf="screen === 'dictionaries' || root"
|
||||||
></a>
|
></a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
class="ml-32 breadcrumb"
|
||||||
|
[routerLink]="'/ui/admin/rules'"
|
||||||
|
[routerLinkActiveOptions]="{ exact: true }"
|
||||||
|
routerLinkActive="active"
|
||||||
|
translate="rule-editor"
|
||||||
|
*ngIf="screen === 'rules' || root"
|
||||||
|
></a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
class="ml-32 breadcrumb"
|
class="ml-32 breadcrumb"
|
||||||
[routerLink]="'/ui/admin/users'"
|
[routerLink]="'/ui/admin/users'"
|
||||||
|
|||||||
@ -61,12 +61,13 @@
|
|||||||
<ace-editor
|
<ace-editor
|
||||||
#editorComponent
|
#editorComponent
|
||||||
[mode]="'text'"
|
[mode]="'text'"
|
||||||
[theme]="'redaction'"
|
[theme]="'eclipse'"
|
||||||
[options]="aceOptions"
|
[options]="aceOptions"
|
||||||
[readOnly]="!permissionsService.isAdmin()"
|
[readOnly]="!permissionsService.isAdmin()"
|
||||||
(textChanged)="textChanged($event)"
|
(textChanged)="textChanged($event)"
|
||||||
[autoUpdateContent]="true"
|
[autoUpdateContent]="true"
|
||||||
[text]="dictionaryEntriesAsText"
|
[text]="dictionaryEntriesAsText"
|
||||||
|
class="ace-redaction"
|
||||||
>
|
>
|
||||||
</ace-editor>
|
</ace-editor>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,19 +3,16 @@
|
|||||||
|
|
||||||
.editor-container {
|
.editor-container {
|
||||||
height: calc(100% - 50px);
|
height: calc(100% - 50px);
|
||||||
width: 100%;
|
}
|
||||||
|
|
||||||
ace-editor {
|
.changes-box {
|
||||||
width: 100%;
|
right: 403px;
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.left-container {
|
.left-container {
|
||||||
width: calc(100vw - 383px);
|
width: calc(100vw - 353px);
|
||||||
height: calc(100vh - 141px);
|
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
box-shadow: inset 0 4px 3px -2px #e2e4e9;
|
@include inset-shadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-container {
|
.right-container {
|
||||||
@ -24,28 +21,6 @@
|
|||||||
padding: 24px;
|
padding: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.changes-box {
|
|
||||||
position: absolute;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
bottom: 40px;
|
|
||||||
right: 403px;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 16px;
|
|
||||||
background-color: $white;
|
|
||||||
box-shadow: 0 2px 6px 0 rgba(40, 50, 65, 0.3);
|
|
||||||
z-index: 5000;
|
|
||||||
}
|
|
||||||
|
|
||||||
ace-editor {
|
|
||||||
box-sizing: border-box;
|
|
||||||
border: 1px solid $grey-5;
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: $white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dictionary-header {
|
.dictionary-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@ -51,7 +51,6 @@ export class DictionaryOverviewScreenComponent {
|
|||||||
private readonly _activatedRoute: ActivatedRoute,
|
private readonly _activatedRoute: ActivatedRoute,
|
||||||
private readonly _appStateService: AppStateService
|
private readonly _appStateService: AppStateService
|
||||||
) {
|
) {
|
||||||
ace.config.set('basePath', '/assets/ace-editor/');
|
|
||||||
this._activatedRoute.params.subscribe((params) => {
|
this._activatedRoute.params.subscribe((params) => {
|
||||||
this.dictionary = this._appStateService.dictionaryData[params.type];
|
this.dictionary = this._appStateService.dictionaryData[params.type];
|
||||||
if (!this.dictionary) {
|
if (!this.dictionary) {
|
||||||
|
|||||||
@ -0,0 +1,49 @@
|
|||||||
|
<section>
|
||||||
|
<div class="page-header">
|
||||||
|
<redaction-admin-breadcrumbs></redaction-admin-breadcrumbs>
|
||||||
|
<div class="actions">
|
||||||
|
<redaction-circle-button
|
||||||
|
class="ml-6"
|
||||||
|
*ngIf="permissionsService.isUser()"
|
||||||
|
[routerLink]="['/ui/projects/']"
|
||||||
|
tooltip="common.close"
|
||||||
|
tooltipPosition="before"
|
||||||
|
icon="red:close"
|
||||||
|
></redaction-circle-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="red-content-inner">
|
||||||
|
<div class="editor-container">
|
||||||
|
<ace-editor
|
||||||
|
#editorComponent
|
||||||
|
[mode]="'java'"
|
||||||
|
[theme]="'eclipse'"
|
||||||
|
[options]="aceOptions"
|
||||||
|
[readOnly]="!permissionsService.isAdmin()"
|
||||||
|
(textChanged)="textChanged($event)"
|
||||||
|
[autoUpdateContent]="true"
|
||||||
|
[text]="rules"
|
||||||
|
class="ace-redaction"
|
||||||
|
>
|
||||||
|
</ace-editor>
|
||||||
|
</div>
|
||||||
|
<div class="changes-box" *ngIf="hasChanges">
|
||||||
|
<redaction-icon-button
|
||||||
|
*ngIf="permissionsService.isAdmin()"
|
||||||
|
icon="red:check"
|
||||||
|
(action)="save()"
|
||||||
|
text="dictionary-overview.save-changes"
|
||||||
|
[primary]="true"
|
||||||
|
></redaction-icon-button>
|
||||||
|
<redaction-icon-button
|
||||||
|
*ngIf="permissionsService.isAdmin()"
|
||||||
|
(action)="revert()"
|
||||||
|
text="dictionary-overview.revert-changes"
|
||||||
|
[linkButton]="true"
|
||||||
|
></redaction-icon-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<redaction-full-page-loading-indicator [displayed]="processing"></redaction-full-page-loading-indicator>
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
@import '../../../../assets/styles/red-mixins';
|
||||||
|
|
||||||
|
.editor-container {
|
||||||
|
width: 100%;
|
||||||
|
padding: 15px;
|
||||||
|
@include inset-shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
.changes-box {
|
||||||
|
right: 40px;
|
||||||
|
}
|
||||||
@ -0,0 +1,98 @@
|
|||||||
|
import { Component, ViewChild } from '@angular/core';
|
||||||
|
import { PermissionsService } from '../../../common/service/permissions.service';
|
||||||
|
import { AceEditorComponent } from 'ng2-ace-editor';
|
||||||
|
import { RulesControllerService } from '@redaction/red-ui-http';
|
||||||
|
import { NotificationService, NotificationType } from '../../../notification/notification.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
declare var ace;
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'redaction-rules-screen',
|
||||||
|
templateUrl: './rules-screen.component.html',
|
||||||
|
styleUrls: ['./rules-screen.component.scss']
|
||||||
|
})
|
||||||
|
export class RulesScreenComponent {
|
||||||
|
public aceOptions = { showPrintMargin: false };
|
||||||
|
public rules: string;
|
||||||
|
public processing = true;
|
||||||
|
|
||||||
|
public initialLines: string[] = [];
|
||||||
|
public currentLines: string[] = [];
|
||||||
|
public changedLines: number[] = [];
|
||||||
|
public activeEditMarkers: any[] = [];
|
||||||
|
|
||||||
|
@ViewChild('editorComponent', { static: true })
|
||||||
|
editorComponent: AceEditorComponent;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly permissionsService: PermissionsService,
|
||||||
|
private readonly _rulesControllerService: RulesControllerService,
|
||||||
|
private readonly _notificationService: NotificationService,
|
||||||
|
private readonly _translateService: TranslateService
|
||||||
|
) {
|
||||||
|
this._initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _initialize() {
|
||||||
|
this._rulesControllerService.downloadRules().subscribe(
|
||||||
|
(rules) => {
|
||||||
|
this.rules = rules.rules;
|
||||||
|
this.revert();
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.processing = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public textChanged($event: any) {
|
||||||
|
this.currentLines = $event.split('\n');
|
||||||
|
this.changedLines = [];
|
||||||
|
this.activeEditMarkers.forEach((am) => {
|
||||||
|
this.editorComponent.getEditor().getSession().removeMarker(am);
|
||||||
|
});
|
||||||
|
this.activeEditMarkers = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < this.currentLines.length; i++) {
|
||||||
|
const currentEntry = this.currentLines[i];
|
||||||
|
if (this.initialLines.indexOf(currentEntry) < 0) {
|
||||||
|
this.changedLines.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Range = ace.require('ace/range').Range;
|
||||||
|
for (const i of this.changedLines) {
|
||||||
|
const entry = this.currentLines[i];
|
||||||
|
if (entry?.trim().length > 0) {
|
||||||
|
// only mark non-empty lines
|
||||||
|
this.activeEditMarkers.push(this.editorComponent.getEditor().getSession().addMarker(new Range(i, 0, i, 1), 'changed-row-marker', 'fullLine'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get hasChanges(): boolean {
|
||||||
|
return this.activeEditMarkers.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async save(): Promise<void> {
|
||||||
|
this.processing = true;
|
||||||
|
this._rulesControllerService.uploadRules({ rules: this.editorComponent.getEditor().getValue() }).subscribe(
|
||||||
|
() => {
|
||||||
|
this._initialize();
|
||||||
|
this._notificationService.showToastNotification(this._translateService.instant('rules-screen.success.generic'), null, NotificationType.SUCCESS);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.processing = false;
|
||||||
|
this._notificationService.showToastNotification(this._translateService.instant('rules-screen.error.generic'), null, NotificationType.ERROR);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public revert(): void {
|
||||||
|
this.initialLines = this.rules.split('\n');
|
||||||
|
this.editorComponent.getEditor().setValue(this.rules);
|
||||||
|
this.editorComponent.getEditor().clearSelection();
|
||||||
|
this.processing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -546,6 +546,14 @@
|
|||||||
},
|
},
|
||||||
"search": "Search..."
|
"search": "Search..."
|
||||||
},
|
},
|
||||||
|
"rules-screen": {
|
||||||
|
"error": {
|
||||||
|
"generic": "Something went wrong... Rules update failed!"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"generic": "Rules updated!"
|
||||||
|
}
|
||||||
|
},
|
||||||
"dictionaries": "Dictionaries",
|
"dictionaries": "Dictionaries",
|
||||||
"user-management": "User Management",
|
"user-management": "User Management",
|
||||||
"notifications": {
|
"notifications": {
|
||||||
@ -554,5 +562,6 @@
|
|||||||
"tomorrow": "Tomorrow",
|
"tomorrow": "Tomorrow",
|
||||||
"mark-read": "Mark as read",
|
"mark-read": "Mark as read",
|
||||||
"mark-unread": "Mark as unread"
|
"mark-unread": "Mark as unread"
|
||||||
}
|
},
|
||||||
|
"rule-editor": "Rule Editor"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,12 +20,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ace-redaction {
|
.ace-redaction {
|
||||||
background-color: $white;
|
|
||||||
color: $accent;
|
color: $accent;
|
||||||
|
|
||||||
.ace_gutter {
|
.ace_gutter {
|
||||||
background: $grey-2;
|
background: $grey-2;
|
||||||
color: $grey-7;
|
color: $grey-7;
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ace_active-line {
|
||||||
|
background: $grey-6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ace_print-margin {
|
.ace_print-margin {
|
||||||
@ -57,3 +61,30 @@
|
|||||||
color: $grey-4;
|
color: $grey-4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editor-container {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
ace-editor {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid $grey-5;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: $white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.changes-box {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
bottom: 40px;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
background-color: $white;
|
||||||
|
box-shadow: 0 2px 6px 0 rgba(40, 50, 65, 0.3);
|
||||||
|
z-index: 5000;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user