From 8adfe426aa352767dddd147796bc284d363552f0 Mon Sep 17 00:00:00 2001 From: Tobias Weber Date: Wed, 16 Jul 2025 21:04:14 +0200 Subject: [PATCH 01/34] Various improvements to the query console --- .../querying/console/console.component.html | 162 +++++++++--------- .../querying/console/console.component.scss | 10 +- .../querying/console/console.component.ts | 149 +++++----------- .../querying/console/query-history.model.ts | 13 +- 4 files changed, 136 insertions(+), 198 deletions(-) diff --git a/src/app/views/querying/console/console.component.html b/src/app/views/querying/console/console.component.html index cdb3e809..d494469f 100644 --- a/src/app/views/querying/console/console.component.html +++ b/src/app/views/querying/console/console.component.html @@ -1,44 +1,75 @@ - - -
-
- Namespace: {{activeNamespace()}} -
- Namespace: - + [ngValue]="namespace.name">{{ namespace.name }} +
-
- - -
-
+ [ngClass]="usesAdvancedConsole() ? 'advanced-console': 'simple-console'">
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
- +
  • -
    History
    +
    + History + +
    @@ -79,71 +112,38 @@
- - - - - - - - - - - - - - - - - - - - - - - - - - -
- - + Click on a query to view its result and additional information.
+ container="body" *ngIf="someExpanded()" + placement="top" delay="500">
- + - {{result.query}} - - {{result.affectedTuples}} - ! - + {{ result.query }} +
+ + {{ result.affectedTuples }} + ! + +
-
Error: diff --git a/src/app/views/querying/console/console.component.scss b/src/app/views/querying/console/console.component.scss index 57d7af54..edd5a705 100644 --- a/src/app/views/querying/console/console.component.scss +++ b/src/app/views/querying/console/console.component.scss @@ -119,7 +119,7 @@ select { #results .card-header:hover span { overflow: auto; max-height: 6em; - transition: max-height 0.5s; + transition: max-height 0.2s; transition-delay: 0.5s; } @@ -137,7 +137,7 @@ select { .del-hist-item { position: absolute; - right: 1em; + left: 1em; top: 50%; margin-top: -1em; display: none; @@ -170,10 +170,6 @@ select { margin-right: 0.5em; } -.document-db-info { - text-align: right; -} - .advanced-console-db-info { display: inline-block; @@ -194,7 +190,7 @@ select { } .advanced-console { - height: 185.617px; + height: calc(240px - 66px); margin: 1px; } diff --git a/src/app/views/querying/console/console.component.ts b/src/app/views/querying/console/console.component.ts index d838a3aa..5d3ea77a 100644 --- a/src/app/views/querying/console/console.component.ts +++ b/src/app/views/querying/console/console.component.ts @@ -1,14 +1,4 @@ -import { - Component, - effect, - inject, - OnDestroy, - OnInit, - signal, - untracked, - ViewChild, - WritableSignal -} from '@angular/core'; +import {Component, computed, inject, OnDestroy, OnInit, Signal, signal, ViewChild, WritableSignal} from '@angular/core'; import {EntityConfig} from '../../../components/data-view/data-table/entity-config'; import {CrudService} from '../../../services/crud.service'; import {RelationalResult, Result} from '../../../components/data-view/models/result-set.model'; @@ -54,7 +44,7 @@ export class ConsoleComponent implements OnInit, OnDestroy { private readonly LOCAL_STORAGE_NAMESPACE_KEY = 'polypheny-namespace'; results: WritableSignal[]> = signal([]); - collapsed: boolean[]; + collapsed: WritableSignal = signal([]); queryAnalysis: InformationPage; analyzeQuery = true; useCache = true; @@ -69,8 +59,11 @@ export class ConsoleComponent implements OnInit, OnDestroy { historySearchQuery = ''; confirmDeletingHistory; readonly activeNamespace: WritableSignal = signal(null); - readonly namespaces: WritableSignal = signal([]); - delayedNamespace: string = null + readonly namespaces: Signal = computed(() => Array.from(this._catalog.namespaces().values())); + readonly usesAdvancedConsole: Signal = computed(() => this.language() === 'mql' || this.language() === 'cypher'); + readonly someExpanded: Signal = computed(() => this.collapsed().some(v => v)); + readonly someCollapsed: Signal = computed(() => this.collapsed().some(v => !v)); + delayedNamespace: string = null; entityConfig: EntityConfig = { create: false, @@ -80,7 +73,6 @@ export class ConsoleComponent implements OnInit, OnDestroy { search: false, exploring: false }; - showNamespaceConfig: boolean; constructor() { this.websocket = new WebSocket(); @@ -94,22 +86,6 @@ export class ConsoleComponent implements OnInit, OnDestroy { } this.initWebsocket(); - - effect(() => { - const namespace = this._catalog.namespaces(); - untracked(() => { - this.namespaces.set(Array.from(namespace.values())); - }); - }); - - effect(() => { - const res = this.results(); - - untracked(() => { - this.collapsed = new Array(res.length); - this.collapsed.fill(false); - }) - }); } @@ -123,12 +99,9 @@ export class ConsoleComponent implements OnInit, OnDestroy { private loadAndSetNamespaceDB() { let namespaceName = localStorage.getItem(this.LOCAL_STORAGE_NAMESPACE_KEY); - if (namespaceName === null || (this.namespaces && this.namespaces.length > 0 && (this.namespaces().filter(n => n.name === namespaceName).length === 0))) { - if (this.namespaces() && this.namespaces().length > 0) { - namespaceName = this.namespaces()[0].name; - } else { - namespaceName = 'public'; - } + const hasNamespaces = this.namespaces() && this.namespaces().length > 0; + if (namespaceName === null || (hasNamespaces && !this.namespaces().some(n => n.name === namespaceName))) { + namespaceName = hasNamespaces ? this.namespaces()[0].name : 'public'; } if (!namespaceName) { return; @@ -156,18 +129,18 @@ export class ConsoleComponent implements OnInit, OnDestroy { return; } if (this.saveInHistory) { - this.addToHistory(code, this.language()); + this.addToHistory(code, this.language(), this.activeNamespace()); } - if (this.usesAdvancedConsole(this.language())) { - code.split(";").forEach((query: string) => { + if (this.usesAdvancedConsole()) { + code.split(';').forEach((query: string) => { // maybe adjust - const graphUse = /use *graph *([a-zA-Z][a-zA-Z0-9-_]*)/gmi + const graphUse = /use *graph *([a-zA-Z][a-zA-Z0-9-_]*)/gmi; const matchGraph = graphUse.exec(query.trim()); if (matchGraph !== null && matchGraph.length > 1) { this.delayedNamespace = matchGraph[1]; } - const useRegex = /use ([a-zA-Z][a-zA-Z0-9-_]*)/gmi + const useRegex = /use ([a-zA-Z][a-zA-Z0-9-_]*)/gmi; const match = useRegex.exec(query.trim()); if (match !== null && match.length > 1) { const namespace = match[1]; @@ -175,7 +148,7 @@ export class ConsoleComponent implements OnInit, OnDestroy { this.delayedNamespace = namespace; } } - }) + }); if (code.match('show db')) { @@ -199,14 +172,15 @@ export class ConsoleComponent implements OnInit, OnDestroy { if (!this._crud.anyQuery(this.websocket, new QueryRequest(code, this.analyzeQuery, this.useCache, this.language(), this.activeNamespace()))) { this.loading.set(false); this.results.set([new RelationalResult('Could not establish a connection with the server.')]); + this.resetCollapsed(); } } collapseAll(collapse: boolean) { - this.collapsed.fill(collapse); + this.collapsed.update(v => v.map(() => collapse)); } - addToHistory(query: string, lang: string): void { + addToHistory(query: string, lang: string, namespace: string): void { if (this.history.size >= this.MAX_HISTORY) { let h: QueryHistory = new QueryHistory(''); this.history.forEach((val, key) => { @@ -216,21 +190,24 @@ export class ConsoleComponent implements OnInit, OnDestroy { }); this.history.delete(h.query); } - const newHistory = new QueryHistory(query, null, lang); + const newHistory = new QueryHistory(query, null, lang, namespace); this.history.set(newHistory.query, newHistory); localStorage.setItem(this.LOCAL_STORAGE_HISTORY_KEY, JSON.stringify(Array.from(this.history.values()))); } - applyHistory(query: string, lang: string, run: boolean) { + applyHistory(query: string, lang: string, namespace: string, run: boolean) { this.language.set(lang); this.codeEditor.setCode(query); + if (namespace) { + this.activeNamespace.set(namespace); + } if (run) { this.submitQuery(); } } - deleteHistoryItem(key, e) { + deleteHistoryItem(key: string, e: MouseEvent) { if (this.confirmDeletingHistory === key) { this.history.delete(key); localStorage.setItem(this.LOCAL_STORAGE_HISTORY_KEY, JSON.stringify(Array.from(this.history.values()))); @@ -323,12 +300,13 @@ export class ConsoleComponent implements OnInit, OnDestroy { } else if (Array.isArray(msg) && ((msg[0].hasOwnProperty('data') || msg[0].hasOwnProperty('affectedTuples') || msg[0].hasOwnProperty('error')))) { // array of ResultSets if (this.delayedNamespace && !msg[0].hasOwnProperty('error')) { this.activeNamespace.set(this.delayedNamespace); - this.storeNamespace(this.delayedNamespace) + this.storeNamespace(this.delayedNamespace); } this.delayedNamespace = null; this.loading.set(false); this.results.set([]>msg); + this.resetCollapsed(); } else if (msg.hasOwnProperty('type')) { //if msg contains a notification of a changed information object const iObj = msg; @@ -359,60 +337,24 @@ export class ConsoleComponent implements OnInit, OnDestroy { this.submitQuery(); } - formatQuery() { - let code = this.codeEditor.getCode(); - if (!code) { - return; - } - let before = ''; - const after = ')'; - - // here we replace the Json incompatible types with placeholders - const temp = code.match(/NumberDecimal\([^)]*\)/g); - - if (temp !== null) { - for (let i = 0; i < temp.length; i++) { - code = code.replace(temp[i], '"___' + i + '"'); - } - } - - - const splits = code.split('('); - before = splits.shift() + '('; - - try { - let json = this.parse(splits.join('(').slice(0, -1)); - // we have to translate them back - if (temp !== null) { - for (let i = 0; i < temp.length; i++) { - json = json.replace('"___' + i + '"', temp[i]); - } - } - - this.codeEditor.setCode(before + json + after); - } catch (e) { - this._toast.warn(e); - } - } - - parse(code: string) { - const formatted = JSON.stringify(JSON.parse('[' + code + ']'), null, 4); - return formatted.substring(1, formatted.length - 1); - } - private storeNamespace(name: string) { localStorage.setItem(this.LOCAL_STORAGE_NAMESPACE_KEY, name); } - toggleCollapsed(i: number) { - console.log(i) - if (this.collapsed !== undefined && this.collapsed[i] !== undefined) { - this.collapsed[i] = !this.collapsed[i]; - } + private resetCollapsed() { + const collapsed = new Array(this.results().length); + collapsed.fill(false); + this.collapsed.set(collapsed); } - clearConsole() { - this.codeEditor.setCode(''); + toggleCollapsed(i: number) { + this.collapsed.update(v => { + const collapsed = [...v]; + if (collapsed[i] !== undefined) { + collapsed[i] = !collapsed[i]; + } + return collapsed; + }); } toggleCache(b: boolean) { @@ -428,14 +370,6 @@ export class ConsoleComponent implements OnInit, OnDestroy { this.originalCache = null; } - usesAdvancedConsole(lang: string) { - return lang === 'mql' || lang === 'cypher'; - } - - toggleNamespaceField() { - this.showNamespaceConfig = !this.showNamespaceConfig; - } - changedDefaultDB(n) { this.activeNamespace.set(n); } @@ -443,4 +377,9 @@ export class ConsoleComponent implements OnInit, OnDestroy { setLanguage(language) { this.language.set(language); } + + clearHistory() { + this.history.clear(); + localStorage.setItem(this.LOCAL_STORAGE_HISTORY_KEY, JSON.stringify(Array.from(this.history.values()))); + } } diff --git a/src/app/views/querying/console/query-history.model.ts b/src/app/views/querying/console/query-history.model.ts index 2a708fb0..d6936306 100644 --- a/src/app/views/querying/console/query-history.model.ts +++ b/src/app/views/querying/console/query-history.model.ts @@ -2,13 +2,16 @@ import * as moment from 'moment'; export class QueryHistory { - query: string; - time: Date; - lang: string; + readonly query: string; + readonly time: Date; + readonly lang: string; + readonly namespace: string; - constructor(query: string, time = null, lang = 'sql') { + + constructor(query: string, time = null, lang = 'sql', namespace = 'public') { this.query = query; this.lang = lang; + this.namespace = namespace; if (time === null) { this.time = new Date(); } else { @@ -24,7 +27,7 @@ export class QueryHistory { for (const key in obj) { if (obj.hasOwnProperty(key)) { - map.set(obj[key].query, new QueryHistory(obj[key].query, obj[key].time, obj[key].lang)); + map.set(obj[key].query, new QueryHistory(obj[key].query, obj[key].time, obj[key].lang, obj[key].namespace)); } } } From 887afff2b2fa882dcbfbeeabf490ac5fd6f41e83 Mon Sep 17 00:00:00 2001 From: Tobias Weber Date: Thu, 17 Jul 2025 15:56:22 +0200 Subject: [PATCH 02/34] Improve look and feel of query history --- .../delete-confirm/delete-confirm.component.html | 4 ++-- .../delete-confirm/delete-confirm.component.scss | 3 +++ .../views/querying/console/console.component.html | 15 +++++---------- .../views/querying/console/console.component.scss | 5 ----- .../views/querying/console/console.component.ts | 13 ++++--------- 5 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/app/components/delete-confirm/delete-confirm.component.html b/src/app/components/delete-confirm/delete-confirm.component.html index e02e5c99..e4c03fe2 100644 --- a/src/app/components/delete-confirm/delete-confirm.component.html +++ b/src/app/components/delete-confirm/delete-confirm.component.html @@ -1,4 +1,4 @@ - diff --git a/src/app/components/delete-confirm/delete-confirm.component.scss b/src/app/components/delete-confirm/delete-confirm.component.scss index 83d078d0..4ed4638b 100644 --- a/src/app/components/delete-confirm/delete-confirm.component.scss +++ b/src/app/components/delete-confirm/delete-confirm.component.scss @@ -2,3 +2,6 @@ cursor: pointer; } +.hover-grow:hover { + transform: scale(1.2); +} diff --git a/src/app/views/querying/console/console.component.html b/src/app/views/querying/console/console.component.html index d494469f..9ecd5d17 100644 --- a/src/app/views/querying/console/console.component.html +++ b/src/app/views/querying/console/console.component.html @@ -6,7 +6,7 @@
+ class="list-group-item header grey-bg px-3 border-bottom">
Namespace: + '{{ activeNamespace() }}' does not exist.
(empty) -
  • @@ -99,16 +100,10 @@ {{ h.value.displayTime() }} {{ h.value.fromNow() }}
  • -
    +
    {{ _util.limitedString( h.value.query ) }}
    - - - - + diff --git a/src/app/views/querying/console/console.component.scss b/src/app/views/querying/console/console.component.scss index edd5a705..a68e05ae 100644 --- a/src/app/views/querying/console/console.component.scss +++ b/src/app/views/querying/console/console.component.scss @@ -136,10 +136,6 @@ select { } .del-hist-item { - position: absolute; - left: 1em; - top: 50%; - margin-top: -1em; display: none; } @@ -182,7 +178,6 @@ select { .grey-bg { background-color: #f0f3f5; border: none; - border-bottom: 1px white solid; } .grey-border { diff --git a/src/app/views/querying/console/console.component.ts b/src/app/views/querying/console/console.component.ts index 5d3ea77a..8cd5c44b 100644 --- a/src/app/views/querying/console/console.component.ts +++ b/src/app/views/querying/console/console.component.ts @@ -57,9 +57,9 @@ export class ConsoleComponent implements OnInit, OnDestroy { saveInHistory = true; showSearch = false; historySearchQuery = ''; - confirmDeletingHistory; readonly activeNamespace: WritableSignal = signal(null); readonly namespaces: Signal = computed(() => Array.from(this._catalog.namespaces().values())); + readonly activeNamespaceExists: Signal = computed(() => this.namespaces().some(v => v.name === this.activeNamespace())); readonly usesAdvancedConsole: Signal = computed(() => this.language() === 'mql' || this.language() === 'cypher'); readonly someExpanded: Signal = computed(() => this.collapsed().some(v => v)); readonly someCollapsed: Signal = computed(() => this.collapsed().some(v => !v)); @@ -207,14 +207,9 @@ export class ConsoleComponent implements OnInit, OnDestroy { } } - deleteHistoryItem(key: string, e: MouseEvent) { - if (this.confirmDeletingHistory === key) { - this.history.delete(key); - localStorage.setItem(this.LOCAL_STORAGE_HISTORY_KEY, JSON.stringify(Array.from(this.history.values()))); - } else { - this.confirmDeletingHistory = key; - } - e.stopPropagation(); + deleteHistoryItem(key: string) { + this.history.delete(key); + localStorage.setItem(this.LOCAL_STORAGE_HISTORY_KEY, JSON.stringify(Array.from(this.history.values()))); } //from: https://stackoverflow.com/questions/52793944/angular-keyvalue-pipe-sort-properties-iterate-in-order From baf65a6e00cbf9edd72aa64a546ce19a207d2ce0 Mon Sep 17 00:00:00 2001 From: Tobias Weber Date: Thu, 17 Jul 2025 16:54:25 +0200 Subject: [PATCH 03/34] Remove unnecessary nested c-containers from views --- .../breadcrumb/breadcrumb.component.html | 64 ++-- .../information-manager.component.html | 4 +- .../edit-notebook.component.html | 4 +- .../edit-notebook.component.scss | 4 +- .../views/dashboard/dashboard.component.html | 322 +++++++++--------- .../form-generator.component.html | 16 +- src/app/views/login/login.component.html | 152 ++++----- .../querying/console/console.component.html | 318 +++++++++-------- .../querying/polyalg/polyalg.component.html | 8 +- .../schema-editing.component.html | 8 +- .../table-view/table-view.component.html | 4 +- 11 files changed, 448 insertions(+), 456 deletions(-) diff --git a/src/app/components/breadcrumb/breadcrumb.component.html b/src/app/components/breadcrumb/breadcrumb.component.html index 1d882920..ffd67f3a 100644 --- a/src/app/components/breadcrumb/breadcrumb.component.html +++ b/src/app/components/breadcrumb/breadcrumb.component.html @@ -1,40 +1,38 @@