From ca6c64388b6605a7bfa7e343746a5bea15a3acc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Tue, 8 Dec 2020 17:01:03 +0200 Subject: [PATCH 1/2] Rules editor --- angular.json | 7 +- apps/red-ui/src/app/app.module.ts | 12 ++- .../admin-breadcrumbs.component.html | 9 ++ .../dictionary-overview-screen.component.html | 3 +- .../dictionary-overview-screen.component.scss | 35 +------ .../dictionary-overview-screen.component.ts | 1 - .../rules-screen/rules-screen.component.html | 49 ++++++++++ .../rules-screen/rules-screen.component.scss | 11 +++ .../rules-screen/rules-screen.component.ts | 98 +++++++++++++++++++ apps/red-ui/src/assets/i18n/en.json | 11 ++- apps/red-ui/src/assets/styles/red-editor.scss | 51 +++++++--- 11 files changed, 240 insertions(+), 47 deletions(-) create mode 100644 apps/red-ui/src/app/screens/admin/rules-screen/rules-screen.component.html create mode 100644 apps/red-ui/src/app/screens/admin/rules-screen/rules-screen.component.scss create mode 100644 apps/red-ui/src/app/screens/admin/rules-screen/rules-screen.component.ts diff --git a/angular.json b/angular.json index 063e876f9..47b307009 100644 --- a/angular.json +++ b/angular.json @@ -36,7 +36,12 @@ "apps/red-ui/src/manifest.webmanifest" ], "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-solarized_light.js" + ] }, "configurations": { "production": { diff --git a/apps/red-ui/src/app/app.module.ts b/apps/red-ui/src/app/app.module.ts index 116926226..b1a4976f2 100644 --- a/apps/red-ui/src/app/app.module.ts +++ b/apps/red-ui/src/app/app.module.ts @@ -92,6 +92,7 @@ import { TeamMembersComponent } from './components/team-members/team-members.com import { AdminBreadcrumbsComponent } from './components/admin-page-header/admin-breadcrumbs.component'; import { UserListingScreenComponent } from './screens/admin/users/user-listing-screen.component'; import { NotificationsComponent } from './components/notifications/notifications.component'; +import { RulesScreenComponent } from './screens/admin/rules-screen/rules-screen.component'; export function HttpLoaderFactory(httpClient: HttpClient) { return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json'); @@ -167,6 +168,14 @@ const routes = [ data: { routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard] } + }, + { + path: 'rules', + component: RulesScreenComponent, + canActivate: [CompositeRouteGuard], + data: { + routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard] + } } ] } @@ -248,7 +257,8 @@ const matImports = [ TeamMembersComponent, AdminBreadcrumbsComponent, UserListingScreenComponent, - NotificationsComponent + NotificationsComponent, + RulesScreenComponent ], imports: [ BrowserModule, diff --git a/apps/red-ui/src/app/components/admin-page-header/admin-breadcrumbs.component.html b/apps/red-ui/src/app/components/admin-page-header/admin-breadcrumbs.component.html index fb513b57a..fbdb934f0 100644 --- a/apps/red-ui/src/app/components/admin-page-header/admin-breadcrumbs.component.html +++ b/apps/red-ui/src/app/components/admin-page-header/admin-breadcrumbs.component.html @@ -8,6 +8,15 @@ *ngIf="screen === 'dictionaries' || root" > + + diff --git a/apps/red-ui/src/app/screens/admin/dictionary-overview-screen/dictionary-overview-screen.component.scss b/apps/red-ui/src/app/screens/admin/dictionary-overview-screen/dictionary-overview-screen.component.scss index 7e7d258be..f7d9ce007 100644 --- a/apps/red-ui/src/app/screens/admin/dictionary-overview-screen/dictionary-overview-screen.component.scss +++ b/apps/red-ui/src/app/screens/admin/dictionary-overview-screen/dictionary-overview-screen.component.scss @@ -3,19 +3,16 @@ .editor-container { height: calc(100% - 50px); - width: 100%; +} - ace-editor { - width: 100%; - height: 100%; - } +.changes-box { + right: 403px; } .left-container { - width: calc(100vw - 383px); - height: calc(100vh - 141px); + width: calc(100vw - 353px); padding: 15px; - box-shadow: inset 0 4px 3px -2px #e2e4e9; + @include inset-shadow; } .right-container { @@ -24,28 +21,6 @@ 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 { display: flex; align-items: center; diff --git a/apps/red-ui/src/app/screens/admin/dictionary-overview-screen/dictionary-overview-screen.component.ts b/apps/red-ui/src/app/screens/admin/dictionary-overview-screen/dictionary-overview-screen.component.ts index 343bcd8d8..0ab7998f2 100644 --- a/apps/red-ui/src/app/screens/admin/dictionary-overview-screen/dictionary-overview-screen.component.ts +++ b/apps/red-ui/src/app/screens/admin/dictionary-overview-screen/dictionary-overview-screen.component.ts @@ -51,7 +51,6 @@ export class DictionaryOverviewScreenComponent { private readonly _activatedRoute: ActivatedRoute, private readonly _appStateService: AppStateService ) { - ace.config.set('basePath', '/assets/ace-editor/'); this._activatedRoute.params.subscribe((params) => { this.dictionary = this._appStateService.dictionaryData[params.type]; if (!this.dictionary) { diff --git a/apps/red-ui/src/app/screens/admin/rules-screen/rules-screen.component.html b/apps/red-ui/src/app/screens/admin/rules-screen/rules-screen.component.html new file mode 100644 index 000000000..484661724 --- /dev/null +++ b/apps/red-ui/src/app/screens/admin/rules-screen/rules-screen.component.html @@ -0,0 +1,49 @@ +
+ + +
+
+ + +
+
+ + +
+
+
+ + diff --git a/apps/red-ui/src/app/screens/admin/rules-screen/rules-screen.component.scss b/apps/red-ui/src/app/screens/admin/rules-screen/rules-screen.component.scss new file mode 100644 index 000000000..5a2233d9b --- /dev/null +++ b/apps/red-ui/src/app/screens/admin/rules-screen/rules-screen.component.scss @@ -0,0 +1,11 @@ +@import '../../../../assets/styles/red-mixins'; + +.editor-container { + width: 100%; + padding: 15px; + @include inset-shadow; +} + +.changes-box { + right: 40px; +} diff --git a/apps/red-ui/src/app/screens/admin/rules-screen/rules-screen.component.ts b/apps/red-ui/src/app/screens/admin/rules-screen/rules-screen.component.ts new file mode 100644 index 000000000..a0070adf5 --- /dev/null +++ b/apps/red-ui/src/app/screens/admin/rules-screen/rules-screen.component.ts @@ -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 { + 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; + } +} diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json index 033bb2dcb..c6eb0f74f 100644 --- a/apps/red-ui/src/assets/i18n/en.json +++ b/apps/red-ui/src/assets/i18n/en.json @@ -546,6 +546,14 @@ }, "search": "Search..." }, + "rules-screen": { + "error": { + "generic": "Something went wrong... Rules update failed!" + }, + "success": { + "generic": "Rules updated!" + } + }, "dictionaries": "Dictionaries", "user-management": "User Management", "notifications": { @@ -554,5 +562,6 @@ "tomorrow": "Tomorrow", "mark-read": "Mark as read", "mark-unread": "Mark as unread" - } + }, + "rule-editor": "Rule Editor" } diff --git a/apps/red-ui/src/assets/styles/red-editor.scss b/apps/red-ui/src/assets/styles/red-editor.scss index e5af43737..5974c50b2 100644 --- a/apps/red-ui/src/assets/styles/red-editor.scss +++ b/apps/red-ui/src/assets/styles/red-editor.scss @@ -20,40 +20,67 @@ } .ace-redaction { - background-color: $white; - color: $accent; + background-color: $white !important; + color: $accent !important; .ace_gutter { - background: $grey-2; - color: $grey-7; + background: $grey-2 !important; + color: $grey-7 !important; } .ace_print-margin { - width: 0; - background: $white; + width: 0 !important; + background: $white !important; } .ace_cursor { - color: $grey-5; + color: $grey-5 !important; } .ace_marker-layer .ace_selection { - background: $grey-4; + background: $grey-4 !important; } .ace_multiselect .ace_selection.ace_start { - box-shadow: 0 0 3px 0 $white; + box-shadow: 0 0 3px 0 $white !important; } .ace_gutter-active-line { - background-color: $grey-4; + background-color: $grey-4 !important; } .ace_marker-layer .ace_selected-word { - border: 1px solid $grey-4; + border: 1px solid $grey-4 !important; } .ace_invisible { - color: $grey-4; + color: $grey-4 !important; } } + +.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; +} From f1cd540de3e4698a80c612b050b8340892b2c0e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Tue, 8 Dec 2020 23:45:43 +0200 Subject: [PATCH 2/2] Editor theme --- angular.json | 7 ++++- apps/red-ui/src/app/app.component.ts | 4 +++ .../dictionary-overview-screen.component.html | 2 +- .../rules-screen/rules-screen.component.html | 2 +- apps/red-ui/src/assets/styles/red-editor.scss | 28 +++++++++++-------- 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/angular.json b/angular.json index 47b307009..1e85832de 100644 --- a/angular.json +++ b/angular.json @@ -33,6 +33,11 @@ "input": "apps/red-ui/src/assets/", "output": "/assets/" }, + { + "glob": "**/*", + "input": "node_modules/ace-builds/src-min/", + "output": "/assets/ace-builds" + }, "apps/red-ui/src/manifest.webmanifest" ], "styles": ["apps/red-ui/src/styles.scss"], @@ -40,7 +45,7 @@ "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-solarized_light.js" + "node_modules/ace-builds/src-min/theme-eclipse.js" ] }, "configurations": { diff --git a/apps/red-ui/src/app/app.component.ts b/apps/red-ui/src/app/app.component.ts index e8bfc9c8b..23d06136d 100644 --- a/apps/red-ui/src/app/app.component.ts +++ b/apps/red-ui/src/app/app.component.ts @@ -1,6 +1,10 @@ import { Component } from '@angular/core'; import { AppLoadStateService } from './utils/app-load-state.service'; +declare var ace; + +ace.config.set('basePath', '/assets/ace-builds/'); + @Component({ selector: 'redaction-root', templateUrl: './app.component.html', diff --git a/apps/red-ui/src/app/screens/admin/dictionary-overview-screen/dictionary-overview-screen.component.html b/apps/red-ui/src/app/screens/admin/dictionary-overview-screen/dictionary-overview-screen.component.html index 9c5ec1ab6..0f59ab022 100644 --- a/apps/red-ui/src/app/screens/admin/dictionary-overview-screen/dictionary-overview-screen.component.html +++ b/apps/red-ui/src/app/screens/admin/dictionary-overview-screen/dictionary-overview-screen.component.html @@ -61,7 +61,7 @@