+
-
+
+
-
- @for (comment of responses$ | async; track comment) {
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ msg.message }}
+
+
+
+
Tenant ID: {{ msg.tenantId }}
+
File ID: {{ msg.fileId }}
+
Dossier ID: {{ msg.dossierId }}
+
Rule Version: {{ msg.ruleVersion }}
+
Analysis Number: {{ msg.analysisNumber }}
+
Timestamp: {{ msg.parsedTimeStamp }}
+
+
-
-
-
-
-
-
-
-
diff --git a/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen/rules-screen.component.scss b/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen/rules-screen.component.scss
index 702bc576e..fa6ec1c96 100644
--- a/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen/rules-screen.component.scss
+++ b/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen/rules-screen.component.scss
@@ -96,3 +96,94 @@ ngx-monaco-editor {
width: 100%;
}
}
+
+.rules-screen-container {
+ display: flex;
+ flex-direction: column;
+ height: 100vh; /* Adjust based on your layout */
+
+ .main-content {
+ flex: 1;
+ overflow: auto;
+ }
+
+ .rules-logger {
+ flex-shrink: 0;
+ transition: max-height 0.3s ease;
+ overflow: hidden;
+ border-top: 1px solid #ccc;
+ background-color: #fafafa;
+
+ &.collapsed {
+ max-height: 50px; /* Height of the header when collapsed */
+ }
+
+ &:not(.collapsed) {
+ max-height: 300px; /* Maximum height when expanded */
+ }
+
+ .logger-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 10px;
+ cursor: pointer;
+
+ h3 {
+ margin: 0;
+ }
+
+ .collapse-button {
+ background: none;
+ border: none;
+ cursor: pointer;
+ outline: none;
+
+ mat-icon {
+ font-size: 24px;
+ }
+ }
+ }
+
+ .logger-messages {
+ overflow-y: auto;
+ height: calc(100% - 50px); /* Subtract header height */
+ padding: 10px;
+
+ .log-entry {
+ padding: 10px;
+ border-bottom: 1px solid #eee;
+
+ .log-header {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 5px;
+
+ .log-level {
+ font-weight: bold;
+ color: #007bff;
+ }
+
+ .log-time {
+ font-size: 0.9em;
+ color: #666;
+ }
+ }
+
+ .log-message {
+ margin-bottom: 5px;
+ font-size: 1em;
+ }
+
+ .log-details {
+ font-size: 0.9em;
+ color: #666;
+
+ div {
+ margin-bottom: 2px;
+ }
+ }
+ }
+ }
+ }
+}
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 46e233f0e..1cb2c8ee0 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
@@ -24,7 +24,8 @@ import { rulesScreenTranslations } from '../../../translations/rules-screen-tran
import ICodeEditor = monaco.editor.ICodeEditor;
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions;
-import { RulesLoggerComponent } from '@components/rules-logger/rules-logger.component';
+import { RulesLoggerService } from '@services/rules-logger.service';
+import { ScrollingModule } from '@angular/cdk/scrolling';
interface SyntaxError {
line: number;
@@ -39,6 +40,19 @@ interface UploadResponse {
deprecatedWarnings: SyntaxError[];
}
+interface RulesLogMessage {
+ tenantId: string;
+ fileId: string;
+ dossierId: string;
+ dossierTemplateId: string;
+ ruleVersion: number;
+ analysisNumber: number;
+ timeStamp: string;
+ logLevel: string;
+ message: string;
+ parsedTimeStamp?: Date;
+}
+
const RULE_VALIDATION_TIMEOUT = 2000;
@Component({
@@ -61,7 +75,7 @@ const RULE_VALIDATION_TIMEOUT = 2000;
NamePipe,
NgForOf,
MatTooltip,
- RulesLoggerComponent,
+ ScrollingModule,
],
})
export default class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
@@ -71,6 +85,7 @@ export default class RulesScreenComponent implements OnInit, ComponentCanDeactiv
readonly #errors = signal
([]);
#ruleValidationTimeout: number = null;
readonly #copilotService = inject(CopilotService);
+ readonly #rulesLoggerService = inject(RulesLoggerService);
readonly #currentUser = getCurrentUser();
protected readonly collapsed = signal(true);
protected readonly IqserTooltipPositions = IqserTooltipPositions;
@@ -88,10 +103,12 @@ export default class RulesScreenComponent implements OnInit, ComponentCanDeactiv
initialLines: string[] = [];
currentLines: string[] = [];
isLeaving = false;
+ messages: RulesLogMessage[] = [];
readonly type = input.required();
readonly numberOfErrors = computed(() => this.#errors().filter(e => !e.warning).length);
readonly numberOfWarnings = computed(() => this.#errors().filter(e => e.warning).length);
readonly responses$ = new BehaviorSubject<{ text: string; date: string }[]>([]);
+ isLogCollapsed = true;
constructor(
readonly permissionsService: PermissionsService,
@@ -114,6 +131,15 @@ export default class RulesScreenComponent implements OnInit, ComponentCanDeactiv
destination: '/app/rules-copilot',
body: JSON.stringify({ prompts: ['manageradmin'] }),
});
+
+ this.#rulesLoggerService
+ .listen('/topic/' + tenant + '/rule-log-events')
+ .pipe(takeUntilDestroyed())
+ .subscribe(message => {
+ message.parsedTimeStamp = this.parseTimestamp(message.timeStamp);
+ this.messages.push(message);
+ this._changeDetectorRef.detectChanges();
+ });
}
set isLeavingPage(isLeaving: boolean) {
@@ -352,4 +378,23 @@ export default class RulesScreenComponent implements OnInit, ComponentCanDeactiv
() => this._loadingService.stop(),
);
}
+
+ parseTimestamp(timestamp: string): Date {
+ let adjustedTimestamp = timestamp.replace('Z', '');
+
+ const dotIndex = adjustedTimestamp.indexOf('.');
+ if (dotIndex !== -1) {
+ adjustedTimestamp = adjustedTimestamp.substring(0, dotIndex);
+ }
+
+ return new Date(adjustedTimestamp);
+ }
+
+ trackByMessageId(index: number, msg: RulesLogMessage): string {
+ return msg.timeStamp; // Assuming timeStamp is unique
+ }
+
+ toggleLogCollapse() {
+ this.isLogCollapsed = !this.isLogCollapsed;
+ }
}
diff --git a/apps/red-ui/src/app/modules/shared/components/editor/editor.component.html b/apps/red-ui/src/app/modules/shared/components/editor/editor.component.html
index 0273a706b..5fa9ba7ee 100644
--- a/apps/red-ui/src/app/modules/shared/components/editor/editor.component.html
+++ b/apps/red-ui/src/app/modules/shared/components/editor/editor.component.html
@@ -8,5 +8,3 @@
>
-
-
diff --git a/apps/red-ui/src/app/modules/shared/components/editor/editor.component.ts b/apps/red-ui/src/app/modules/shared/components/editor/editor.component.ts
index 7cdb030e6..18338bd46 100644
--- a/apps/red-ui/src/app/modules/shared/components/editor/editor.component.ts
+++ b/apps/red-ui/src/app/modules/shared/components/editor/editor.component.ts
@@ -7,14 +7,13 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { List, OnChange } from '@iqser/common-ui/lib/utils';
import { MonacoEditorModule, MonacoStandaloneCodeEditor, MonacoStandaloneDiffEditor } from '@materia-ui/ngx-monaco-editor';
import { FormsModule } from '@angular/forms';
-import { NgIf } from '@angular/common';
import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions;
import IDiffEditor = monaco.editor.IDiffEditor;
import ICodeEditor = monaco.editor.ICodeEditor;
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
import ILineChange = monaco.editor.ILineChange;
import IEditorMouseEvent = monaco.editor.IEditorMouseEvent;
-import { RulesLoggerComponent } from '@components/rules-logger/rules-logger.component';
+import { NgIf } from '@angular/common';
const MIN_WORD_LENGTH = 2;
const lineChangeToDecoration = ({ originalEndLineNumber, originalStartLineNumber }: ILineChange) =>
@@ -33,7 +32,7 @@ const notZero = (lineChange: ILineChange) => lineChange.originalEndLineNumber !=
templateUrl: './editor.component.html',
styleUrls: ['./editor.component.scss'],
standalone: true,
- imports: [MonacoEditorModule, FormsModule, NgIf, RulesLoggerComponent],
+ imports: [MonacoEditorModule, FormsModule, NgIf],
})
export class EditorComponent implements OnInit, OnChanges {
@Input() showDiffEditor = false;