From 00dd7963c99f104d4ccad37f471277472148f569 Mon Sep 17 00:00:00 2001 From: George Date: Tue, 5 Sep 2023 19:43:20 +0300 Subject: [PATCH] DM-408, add error highlighting for rule edits --- .../rules-screen/rules-screen.component.ts | 63 +++++++++++++++++-- apps/red-ui/src/styles.scss | 5 ++ 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen/rules-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen/rules-screen.component.ts index ef81e18c5..a3020e91b 100644 --- a/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen/rules-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen/rules-screen.component.ts @@ -13,27 +13,35 @@ import ICodeEditor = monaco.editor.ICodeEditor; import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration; import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions; +interface SyntaxError { + line: number; + column: number; + message: string; +} + @Component({ templateUrl: './rules-screen.component.html', styleUrls: ['./rules-screen.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class RulesScreenComponent implements OnInit, ComponentCanDeactivate { - @ViewChild('fileInput') - private _fileInput: ElementRef; - private _codeEditor: ICodeEditor; - private _decorations: string[] = []; - readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID); readonly iconButtonTypes = IconButtonTypes; readonly editorOptions: IStandaloneEditorConstructionOptions = { theme: 'vs', language: 'java', automaticLayout: true, readOnly: !this.permissionsService.canEditRules(), + glyphMargin: true, }; initialLines: string[] = []; currentLines: string[] = []; isLeaving = false; + @ViewChild('fileInput') + private _fileInput: ElementRef; + private _codeEditor: ICodeEditor; + private _decorations: string[] = []; + private _errorGlyphs: string[] = []; + readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID); constructor( readonly permissionsService: PermissionsService, @@ -92,9 +100,12 @@ export class RulesScreenComponent implements OnInit, ComponentCanDeactivate { ).then( async () => { await this._initialize(); + this._removeErrorMarkers(); this._toaster.success(_('rules-screen.success.generic')); }, - () => { + error => { + const errors = error.error as SyntaxError[] | undefined; + this._drawErrorMarkers(errors); this._loadingService.stop(); this._toaster.error(_('rules-screen.error.generic')); }, @@ -104,6 +115,7 @@ export class RulesScreenComponent implements OnInit, ComponentCanDeactivate { revert(): void { this.currentLines = this.initialLines; this._decorations = this._codeEditor?.deltaDecorations(this._decorations, []) || []; + this._removeErrorMarkers(); this._changeDetectorRef.detectChanges(); this._loadingService.stop(); } @@ -142,6 +154,45 @@ export class RulesScreenComponent implements OnInit, ComponentCanDeactivate { } as IModelDeltaDecoration; } + private _drawErrorMarkers(errors: SyntaxError[] | undefined) { + const model = this._codeEditor?.getModel(); + if (!model || !errors?.length) { + return; + } + const markers = []; + const glyphs = []; + errors + .filter(e => e.line > 0) + .forEach(e => { + const endColumn = model.getLineLength(e.line) + 1; + markers.push({ + message: e.message, + severity: monaco.MarkerSeverity.Error, + startLineNumber: e.line, + startColumn: e.column, + endLineNumber: e.line, + endColumn, + }); + glyphs.push({ + range: new monaco.Range(e.line, e.column, e.line, endColumn), + options: { + glyphMarginClassName: 'error-glyph-margin', + }, + }); + }); + this._errorGlyphs = this._codeEditor.deltaDecorations(this._errorGlyphs, glyphs); + (window as any).monaco.editor.setModelMarkers(model, model.id, markers); + } + + private _removeErrorMarkers() { + const model = this._codeEditor?.getModel(); + if (!model) { + return; + } + (window as any).monaco.editor.setModelMarkers(model, model.id, []); + this._errorGlyphs = this._codeEditor?.deltaDecorations(this._errorGlyphs, []) || []; + } + private async _initialize() { this._loadingService.start(); await firstValueFrom(this._rulesService.download(this.#dossierTemplateId)).then( diff --git a/apps/red-ui/src/styles.scss b/apps/red-ui/src/styles.scss index d72aa65ab..43bd48938 100644 --- a/apps/red-ui/src/styles.scss +++ b/apps/red-ui/src/styles.scss @@ -39,6 +39,11 @@ src: url('./assets/styles/fonts/Inter-VariableFont.ttf') format('truetype'); } +.error-glyph-margin { + background: url('./assets/icons/general/alert-circle.svg') no-repeat center; + background-size: 80%; +} + @include common-variables.configureLight( $iqser-primary: vars.$primary, $iqser-primary-rgb: common-functions.hexToRgb(vars.$primary),