Browse Source

UI: refactor unify styles for Audit Log and Event details dialogs

pull/14653/head
Vladyslav_Prykhodko 6 months ago
parent
commit
e81766f3cd
  1. 13
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.html
  2. 26
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.scss
  3. 99
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts
  4. 4
      ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.html
  5. 23
      ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.scss
  6. 103
      ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.ts
  7. 88
      ui-ngx/src/app/shared/components/json-object-view.component.ts
  8. 16
      ui-ngx/src/app/shared/import-export/import-dialog-csv.component.ts
  9. 41
      ui-ngx/src/app/shared/models/ace/ace.models.ts

13
ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.html

@ -24,13 +24,14 @@
<mat-icon class="material-icons">close</mat-icon>
</button>
</mat-toolbar>
<div mat-dialog-content class="flex flex-col">
<label translate class="tb-title no-padding">audit-log.action-data</label>
<div #actionDataEditor class="tb-audit-log-action-data">
<div mat-dialog-content class="flex flex-col gap-8" style="padding:16px;">
<div class="flex max-h-fit min-h-0 flex-1 flex-col">
<span translate class="tb-form-panel-hint">audit-log.action-data</span>
<div #actionDataEditor class="tb-audit-log size-full min-h-12 max-w-full"></div>
</div>
<span style="min-height: 30px;"></span>
<label [class.!hidden]="!displayFailureDetails" translate class="tb-title no-padding">audit-log.failure-details</label>
<div #failureDetailsEditor [class.!hidden]="!displayFailureDetails" class="tb-audit-log-failure-details">
<div class="flex max-h-fit min-h-0 flex-1 flex-col" [class.!hidden]="!displayFailureDetails">
<span translate class="tb-form-panel-hint">audit-log.failure-details</span>
<div #failureDetailsEditor class="tb-audit-log size-full min-h-12 max-w-full"></div>
</div>
</div>
<div mat-dialog-actions class="flex items-center justify-end">

26
ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.scss

@ -13,13 +13,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@import '../../../../../scss/constants';
:host {
.tb-audit-log-action-data,
.tb-audit-log-failure-details {
width: 100%;
min-width: 400px;
height: 100%;
min-height: 50px;
display: grid;
height: 100%;
max-width: 100%;
max-height: 100vh;
grid-template-rows: min-content minmax(auto, 1fr) min-content;
@media #{$mat-gt-xs} {
.mat-mdc-dialog-content {
max-height: 80vh;
}
}
.tb-audit-log {
min-width: max(400px, 100%);
border: 1px solid #c0c0c0;
@media #{$mat-xs} {
min-width: 100%;
}
}
}

99
ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts

@ -22,7 +22,11 @@ import { ActionStatus, AuditLog } from '@shared/models/audit-log.models';
import { Ace } from 'ace-builds';
import { DialogComponent } from '@shared/components/dialog.component';
import { Router } from '@angular/router';
import { getAce } from '@shared/models/ace/ace.models';
import { getAce, updateEditorSize } from '@shared/models/ace/ace.models';
import { Observable, of } from 'rxjs';
import { isObject } from '@core/utils';
import { ContentType, contentTypesMap } from '@shared/models/constants';
import { beautifyJs } from '@shared/models/beautify.models';
export interface AuditLogDetailsDialogData {
auditLog: AuditLog;
@ -41,11 +45,10 @@ export class AuditLogDetailsDialogComponent extends DialogComponent<AuditLogDeta
@ViewChild('failureDetailsEditor', {static: true})
failureDetailsEditorElmRef: ElementRef;
auditLog: AuditLog;
displayFailureDetails: boolean;
actionData: string;
actionFailureDetails: string;
aceEditors: Ace.Editor[] = [];
private auditLog: AuditLog;
private aceEditors: Ace.Editor[] = [];
constructor(protected store: Store<AppState>,
protected router: Router,
@ -58,12 +61,10 @@ export class AuditLogDetailsDialogComponent extends DialogComponent<AuditLogDeta
ngOnInit(): void {
this.auditLog = this.data.auditLog;
this.displayFailureDetails = this.auditLog.actionStatus === ActionStatus.FAILURE;
this.actionData = this.auditLog.actionData ? JSON.stringify(this.auditLog.actionData, null, 2) : '';
this.actionFailureDetails = this.auditLog.actionFailureDetails;
this.createEditor(this.actionDataEditorElmRef, this.actionData);
this.createEditor(this.actionDataEditorElmRef, this.auditLog.actionData);
if (this.displayFailureDetails) {
this.createEditor(this.failureDetailsEditorElmRef, this.actionFailureDetails);
this.createEditor(this.failureDetailsEditorElmRef, this.auditLog.actionFailureDetails);
}
}
@ -72,53 +73,43 @@ export class AuditLogDetailsDialogComponent extends DialogComponent<AuditLogDeta
super.ngOnDestroy();
}
createEditor(editorElementRef: ElementRef, content: string): void {
createEditor(editorElementRef: ElementRef, content: string | object): void {
const editorElement = editorElementRef.nativeElement;
let editorOptions: Partial<Ace.EditorOptions> = {
mode: 'ace/mode/java',
theme: 'ace/theme/github',
showGutter: false,
showPrintMargin: false,
readOnly: true
};
const advancedOptions = {
enableSnippets: false,
enableBasicAutocompletion: false,
enableLiveAutocompletion: false
};
editorOptions = {...editorOptions, ...advancedOptions};
getAce().subscribe(
(ace) => {
const editor = ace.edit(editorElement, editorOptions);
this.aceEditors.push(editor);
editor.session.setUseWrapMode(false);
editor.setValue(content, -1);
this.updateEditorSize(editorElement, content, editor);
}
);
}
updateEditorSize(editorElement: any, content: string, editor: Ace.Editor) {
let newHeight = 200;
let newWidth = 600;
if (content && content.length > 0) {
const lines = content.split('\n');
newHeight = 18 * lines.length + 16;
let maxLineLength = 0;
lines.forEach((row) => {
const line = row.replace(/\t/g, ' ').replace(/\n/g, '');
const lineLength = line.length;
maxLineLength = Math.max(maxLineLength, lineLength);
});
newWidth = 9 * maxLineLength + 16;
let mode = 'java';
let content$: Observable<string> = null;
let contentType = ContentType.TEXT;
if (content && isObject(content)) {
contentType = ContentType.JSON;
mode = contentTypesMap.get(contentType).code;
content$ = beautifyJs(JSON.stringify(content), {indent_size: 2});
}
if (!content$) {
content$ = of(content as string);
}
// newHeight = Math.min(400, newHeight);
this.renderer.setStyle(editorElement, 'minHeight', newHeight.toString() + 'px');
this.renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px');
this.renderer.setStyle(editorElement, 'width', newWidth.toString() + 'px');
editor.resize();
content$.subscribe((processedContent) => {
const isJSON = contentType === ContentType.JSON
const editorOptions: Partial<Ace.EditorOptions> = {
mode: `ace/mode/${mode}`,
theme: 'ace/theme/github',
showGutter: isJSON,
showFoldWidgets: true,
foldStyle: 'markbeginend',
showPrintMargin: false,
readOnly: true,
enableSnippets: false,
enableBasicAutocompletion: false,
enableLiveAutocompletion: false,
};
getAce().subscribe(
(ace) => {
const editor = ace.edit(editorElement, editorOptions);
this.aceEditors.push(editor);
editor.session.setUseWrapMode(false);
editor.setValue(processedContent, -1);
updateEditorSize(editorElement, processedContent, editor, this.renderer, {showGutter: isJSON});
}
)
});
}
}

4
ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.html

@ -24,8 +24,8 @@
<mat-icon class="material-icons">close</mat-icon>
</button>
</mat-toolbar>
<div mat-dialog-content class="flex flex-col">
<div #eventContentEditor class="tb-event-content">
<div mat-dialog-content class="flex flex-col" style="padding:16px;">
<div #eventContentEditor class="tb-event-content size-full min-h-12 max-w-full" [class.border]="showBorder">
</div>
</div>
<div mat-dialog-actions class="flex flex-row">

23
ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.scss

@ -13,11 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@import '../../../../../scss/constants';
:host {
display: grid;
height: 100%;
max-width: 100%;
max-height: 100vh;
grid-template-rows: min-content minmax(auto, 1fr) min-content;
@media #{$mat-gt-xs} {
.mat-mdc-dialog-content {
max-height: 80vh;
}
}
.tb-event-content {
width: 100%;
min-width: 400px;
height: 100%;
min-height: 50px;
&.border {
border: 1px solid #c0c0c0;
}
@media #{$mat-xs} {
min-width: 100%;
}
}
}

103
ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.ts

@ -23,7 +23,7 @@ import { Ace } from 'ace-builds';
import { DialogComponent } from '@shared/components/dialog.component';
import { Router } from '@angular/router';
import { ContentType, contentTypesMap } from '@shared/models/constants';
import { getAce } from '@shared/models/ace/ace.models';
import { getAce, updateEditorSize } from '@shared/models/ace/ace.models';
import { Observable } from 'rxjs/internal/Observable';
import { beautifyJs } from '@shared/models/beautify.models';
import { of } from 'rxjs';
@ -40,40 +40,38 @@ export interface EventContentDialogData {
templateUrl: './event-content-dialog.component.html',
styleUrls: ['./event-content-dialog.component.scss']
})
export class EventContentDialogComponent extends DialogComponent<EventContentDialogData> implements OnInit, OnDestroy {
export class EventContentDialogComponent extends DialogComponent<EventContentDialogComponent> implements OnInit, OnDestroy {
@ViewChild('eventContentEditor', {static: true})
eventContentEditorElmRef: ElementRef;
content: string;
title: string;
contentType: ContentType;
aceEditor: Ace.Editor;
showBorder = false;
private contentType: ContentType;
private aceEditor: Ace.Editor;
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: EventContentDialogData,
public dialogRef: MatDialogRef<EventContentDialogComponent>,
protected dialogRef: MatDialogRef<EventContentDialogComponent>,
private renderer: Renderer2) {
super(store, router, dialogRef);
}
ngOnInit(): void {
this.content = this.data.content;
this.title = this.data.title;
this.contentType = this.data.contentType;
this.createEditor(this.eventContentEditorElmRef, this.content);
this.createEditor(this.eventContentEditorElmRef, this.data.content);
}
ngOnDestroy(): void {
if (this.aceEditor) {
this.aceEditor.destroy();
}
this.aceEditor?.destroy();
super.ngOnDestroy();
}
isJson(str) {
private isJson(str: string) {
try {
return isLiteralObject(JSON.parse(str));
} catch (e) {
@ -81,78 +79,53 @@ export class EventContentDialogComponent extends DialogComponent<EventContentDia
}
}
createEditor(editorElementRef: ElementRef, content: string) {
private createEditor(editorElementRef: ElementRef, content: string) {
const editorElement = editorElementRef.nativeElement;
let mode = 'java';
let content$: Observable<string> = null;
if (this.contentType) {
mode = contentTypesMap.get(this.contentType).code;
if (this.contentType === ContentType.JSON && content) {
content$ = beautifyJs(content, {indent_size: 4});
content$ = beautifyJs(content, {indent_size: 2});
} else if (this.contentType === ContentType.BINARY && content) {
try {
const decodedData = base64toString(content);
if (this.isJson(decodedData)) {
mode = 'json';
content$ = beautifyJs(decodedData, {indent_size: 4});
content$ = beautifyJs(decodedData, {indent_size: 2});
} else {
content$ = of(decodedData);
}
} catch (e) {}
} catch (e) {/**/}
}
}
if (!content$) {
content$ = of(content);
}
content$.subscribe(
(processedContent) => {
let editorOptions: Partial<Ace.EditorOptions> = {
mode: `ace/mode/${mode}`,
theme: 'ace/theme/github',
showGutter: false,
showPrintMargin: false,
readOnly: true
};
const advancedOptions = {
enableSnippets: false,
enableBasicAutocompletion: false,
enableLiveAutocompletion: false
};
editorOptions = {...editorOptions, ...advancedOptions};
getAce().subscribe(
(ace) => {
this.aceEditor = ace.edit(editorElement, editorOptions);
this.aceEditor.session.setUseWrapMode(false);
this.aceEditor.setValue(processedContent, -1);
this.updateEditorSize(editorElement, processedContent, this.aceEditor);
}
);
}
);
}
updateEditorSize(editorElement: any, content: string, editor: Ace.Editor) {
let newHeight = 400;
let newWidth = 600;
if (content && content.length > 0) {
const lines = content.split('\n');
newHeight = 17 * lines.length + 16;
let maxLineLength = 0;
lines.forEach((row) => {
const line = row.replace(/\t/g, ' ').replace(/\n/g, '');
const lineLength = line.length;
maxLineLength = Math.max(maxLineLength, lineLength);
});
newWidth = 9 * maxLineLength + 16;
}
// newHeight = Math.min(400, newHeight);
this.renderer.setStyle(editorElement, 'minHeight', newHeight.toString() + 'px');
this.renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px');
this.renderer.setStyle(editorElement, 'width', newWidth.toString() + 'px');
editor.resize();
content$.subscribe((processedContent) => {
const isJSON = mode === 'json' && processedContent !== '{}';
this.showBorder = isJSON;
const editorOptions: Partial<Ace.EditorOptions> = {
mode: `ace/mode/${mode}`,
theme: 'ace/theme/github',
showGutter: isJSON,
showFoldWidgets: true,
foldStyle: 'markbeginend',
showPrintMargin: false,
readOnly: true,
enableSnippets: false,
enableBasicAutocompletion: false,
enableLiveAutocompletion: false,
};
getAce().subscribe(
(ace) => {
this.aceEditor = ace.edit(editorElement, editorOptions);
this.aceEditor.session.setUseWrapMode(false);
this.aceEditor.setValue(processedContent, -1);
updateEditorSize(editorElement, processedContent, this.aceEditor, this.renderer, {showGutter: isJSON});
}
)
});
}
}

88
ui-ngx/src/app/shared/components/json-object-view.component.ts

@ -15,14 +15,11 @@
///
import { Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, Renderer2, ViewChild } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Ace } from 'ace-builds';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { RafService } from '@core/services/raf.service';
import { isDefinedAndNotNull, isUndefined } from '@core/utils';
import { getAce } from '@shared/models/ace/ace.models';
import { getAce, updateEditorSize } from '@shared/models/ace/ace.models';
import { coerceBoolean } from '@shared/decorators/coercion';
@Component({
selector: 'tb-json-object-view',
@ -36,7 +33,7 @@ import { getAce } from '@shared/models/ace/ace.models';
}
]
})
export class JsonObjectViewComponent implements OnInit, OnDestroy {
export class JsonObjectViewComponent implements OnInit, OnDestroy, ControlValueAccessor {
@ViewChild('jsonViewer', {static: true})
jsonViewerElmRef: ElementRef;
@ -55,96 +52,54 @@ export class JsonObjectViewComponent implements OnInit, OnDestroy {
@Input() sort: (key: string, value: any) => any;
private widthValue: boolean;
get autoWidth(): boolean {
return this.widthValue;
}
@Input()
set autoWidth(value: boolean) {
this.widthValue = coerceBooleanProperty(value);
}
private heigthValue: boolean;
get autoHeight(): boolean {
return this.heigthValue;
}
@coerceBoolean()
autoWidth: boolean
@Input()
set autoHeight(value: boolean) {
this.heigthValue = coerceBooleanProperty(value);
}
@coerceBoolean()
autoHeight: boolean
constructor(public elementRef: ElementRef,
protected store: Store<AppState>,
private raf: RafService,
private renderer: Renderer2) {
constructor(private renderer: Renderer2) {
}
ngOnInit(): void {
this.viewerElement = this.jsonViewerElmRef.nativeElement;
let editorOptions: Partial<Ace.EditorOptions> = {
const editorOptions: Partial<Ace.EditorOptions> = {
mode: 'ace/mode/java',
theme: 'ace/theme/github',
showGutter: false,
showPrintMargin: false,
readOnly: true
};
const advancedOptions = {
readOnly: true,
enableSnippets: false,
enableBasicAutocompletion: false,
enableLiveAutocompletion: false
};
editorOptions = {...editorOptions, ...advancedOptions};
getAce().subscribe(
(ace) => {
this.jsonViewer = ace.edit(this.viewerElement, editorOptions);
this.jsonViewer.session.setUseWrapMode(false);
this.jsonViewer.setValue(this.contentValue ? this.contentValue : '', -1);
if (this.contentValue && (this.widthValue || this.heigthValue)) {
this.updateEditorSize(this.viewerElement, this.contentValue, this.jsonViewer);
if (this.contentValue && (this.autoWidth || this.autoHeight)) {
updateEditorSize(this.viewerElement, this.contentValue, this.jsonViewer, this.renderer, {
ignoreHeight: !this.autoHeight,
ignoreWidth: !this.autoWidth
});
}
}
);
}
ngOnDestroy(): void {
if (this.jsonViewer) {
this.jsonViewer.destroy();
}
this.jsonViewer?.destroy();
}
updateEditorSize(editorElement: any, content: string, editor: Ace.Editor) {
let newHeight = 200;
let newWidth = 600;
if (content && content.length > 0) {
const lines = content.split('\n');
newHeight = 17 * lines.length + 17;
let maxLineLength = 0;
lines.forEach((row) => {
const line = row.replace(/\t/g, ' ').replace(/\n/g, '');
const lineLength = line.length;
maxLineLength = Math.max(maxLineLength, lineLength);
});
newWidth = 8 * maxLineLength + 16;
}
if (this.heigthValue) {
this.renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px');
}
if (this.widthValue) {
this.renderer.setStyle(editorElement, 'width', newWidth.toString() + 'px');
}
editor.resize();
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
registerOnTouched(_fn: any): void {
}
writeValue(value: any): void {
@ -162,8 +117,11 @@ export class JsonObjectViewComponent implements OnInit, OnDestroy {
}
if (this.jsonViewer) {
this.jsonViewer.setValue(this.contentValue ? this.contentValue : '', -1);
if (this.contentValue && (this.widthValue || this.heigthValue)) {
this.updateEditorSize(this.viewerElement, this.contentValue, this.jsonViewer);
if (this.contentValue && (this.autoWidth || this.autoHeight)) {
updateEditorSize(this.viewerElement, this.contentValue, this.jsonViewer, this.renderer, {
ignoreHeight: !this.autoHeight,
ignoreWidth: !this.autoWidth
});
}
}
}

16
ui-ngx/src/app/shared/import-export/import-dialog-csv.component.ts

@ -39,7 +39,7 @@ import {
import { ImportExportService } from '@shared/import-export/import-export.service';
import { TableColumnsAssignmentComponent } from '@shared/import-export/table-columns-assignment.component';
import { Ace } from 'ace-builds';
import { getAce } from '@shared/models/ace/ace.models';
import { getAce, updateEditorSize } from '@shared/models/ace/ace.models';
export interface ImportDialogCsvData {
entityType: EntityType;
@ -283,21 +283,9 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom
this.aceEditor = ace.edit(editorElement, editorOptions);
this.aceEditor.session.setUseWrapMode(false);
this.aceEditor.setValue(content, -1);
this.updateEditorSize(editorElement, content, this.aceEditor);
updateEditorSize(editorElement, content, this.aceEditor, this.renderer, {setMinHeight: true, ignoreWidth: true});
}
);
}
private updateEditorSize(editorElement: any, content: string, editor: Ace.Editor) {
let newHeight = 200;
if (content && content.length > 0) {
const lines = content.split('\n');
newHeight = 16 * lines.length + 24;
}
const minHeight = Math.min(200, newHeight);
this.renderer.setStyle(editorElement, 'minHeight', minHeight.toString() + 'px');
this.renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px');
editor.resize();
}
}

41
ui-ngx/src/app/shared/models/ace/ace.models.ts

@ -19,6 +19,7 @@ import { Observable } from 'rxjs/internal/Observable';
import { forkJoin, from, of } from 'rxjs';
import { map, mergeMap, tap } from 'rxjs/operators';
import { unwrapModule } from '@core/utils';
import { Renderer2 } from '@angular/core';
let aceDependenciesLoaded = false;
let aceModule: any;
@ -98,6 +99,46 @@ export function getAceDiff(): Observable<any> {
}
}
export function updateEditorSize(editorElement: any, content: string, editor: Ace.Editor, renderer: Renderer2, options?: {
showGutter?: boolean,
ignoreHeight?: boolean,
ignoreWidth?: boolean,
setMinHeight?: boolean
}): void {
let newHeight = 200;
let newWidth = 600;
if (content && content.length > 0) {
if (editor.renderer.lineHeight <= 0) {
editor.renderer.updateFull(true);
}
const lines = content.split('\n');
newHeight = editor.renderer.lineHeight * lines.length + 16;
let maxLineLength = 0;
lines.forEach((row) => {
const line = row.replace(/\t/g, ' ').replace(/\n/g, '');
const lineLength = line.length;
maxLineLength = Math.max(maxLineLength, lineLength);
});
if (options?.showGutter) {
maxLineLength += lines.length.toString().length;
}
newWidth = 10 * maxLineLength + 16;
if (options?.showGutter) {
newWidth += 32;
}
}
if (!options.ignoreHeight) {
renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px');
}
if (options.setMinHeight) {
renderer.setStyle(editorElement, 'minHeight', newHeight.toString() + 'px');
}
if (!options.ignoreWidth) {
renderer.setStyle(editorElement, 'width', newWidth.toString() + 'px');
}
editor.resize();
}
export class Range implements Ace.Range {
public start: Ace.Point;

Loading…
Cancel
Save