From 7b2b8598d25177cbb13e8188ce5bc56c962dbb7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Borz=C3=AC?= Date: Mon, 23 Jun 2025 14:18:46 +0200 Subject: [PATCH 1/5] refactor(quest-preview): extract objectives subcomponent --- libs/features/quest/src/index.ts | 1 + .../quest-objectives.component.html | 39 ++++++++++++++ .../quest-objectives.component.scss | 0 .../quest-objectives.component.ts | 35 ++++++++++++ .../quest-preview.component.html | 54 +++---------------- .../quest-preview/quest-preview.component.ts | 49 ++++++++++++++++- 6 files changed, 130 insertions(+), 48 deletions(-) create mode 100644 libs/features/quest/src/quest-preview/quest-objectives.component.html create mode 100644 libs/features/quest/src/quest-preview/quest-objectives.component.scss create mode 100644 libs/features/quest/src/quest-preview/quest-objectives.component.ts diff --git a/libs/features/quest/src/index.ts b/libs/features/quest/src/index.ts index 9a7c36713fb..792d404928f 100644 --- a/libs/features/quest/src/index.ts +++ b/libs/features/quest/src/index.ts @@ -4,6 +4,7 @@ export * from './gameobject-questender/gameobject-questender.component'; export * from './gameobject-queststarter/gameobject-queststarter.component'; export * from './quest-offer-reward/quest-offer-reward.component'; export * from './quest-preview/quest-preview.component'; +export * from './quest-preview/quest-objectives.component'; export * from './quest-request-items/quest-request-items.component'; export * from './quest-template/quest-template.component'; export * from './quest-template-addon/quest-template-addon.component'; diff --git a/libs/features/quest/src/quest-preview/quest-objectives.component.html b/libs/features/quest/src/quest-preview/quest-objectives.component.html new file mode 100644 index 00000000000..c1ff30b1d23 --- /dev/null +++ b/libs/features/quest/src/quest-preview/quest-objectives.component.html @@ -0,0 +1,39 @@ +
+

Objectives

+ +
+ {{ objectiveText() }} +
+ @if (areaDescription()) { +

+ {{ areaDescription() }} +

+ } + +
+ @for (obj of npcObjectives(); track obj) { +

+ • {{ obj.text$ | async }} {{ obj.count }} +

+ } +
+ +
+ @for (obj of itemObjectives(); track obj) { +

+ •   + {{ obj.name$ | async }} + {{ obj.count }} +

+ } +
+ +
+ @for (f of factionRequirements(); track f) { +

+ • {{ f.name$ | async }} + {{ f.value }} +

+ } +
+
diff --git a/libs/features/quest/src/quest-preview/quest-objectives.component.scss b/libs/features/quest/src/quest-preview/quest-objectives.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libs/features/quest/src/quest-preview/quest-objectives.component.ts b/libs/features/quest/src/quest-preview/quest-objectives.component.ts new file mode 100644 index 00000000000..2e967e06c39 --- /dev/null +++ b/libs/features/quest/src/quest-preview/quest-objectives.component.ts @@ -0,0 +1,35 @@ +import { AsyncPipe } from '@angular/common'; +import { ChangeDetectionStrategy, Component, input } from '@angular/core'; +import { IconComponent } from '@keira/shared/base-editor-components'; + +export interface NpcOrGoObjective { + text$: Promise; + count: string; +} + +export interface ItemObjective { + id: number; + name$: Promise; + count: string; +} + +export interface FactionRequirement { + name$: Promise; + value: string; +} + +@Component({ + selector: 'keira-quest-objectives', + templateUrl: './quest-objectives.component.html', + styleUrls: ['./quest-objectives.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [IconComponent, AsyncPipe], +}) +export class QuestObjectivesComponent { + readonly objectiveText = input.required(); + readonly areaDescription = input(); + readonly npcObjectives = input([]); + readonly itemObjectives = input([]); + readonly factionRequirements = input([]); +} diff --git a/libs/features/quest/src/quest-preview/quest-preview.component.html b/libs/features/quest/src/quest-preview/quest-preview.component.html index 4a7ce1b5a20..a391aa03eee 100644 --- a/libs/features/quest/src/quest-preview/quest-preview.component.html +++ b/libs/features/quest/src/quest-preview/quest-preview.component.html @@ -186,53 +186,13 @@
{{ service.title }}
} -
-

Objectives

- -
- {{ service.objectiveText }} -
- @if (!!service.questTemplate.AreaDescription) { -

- {{ service.questTemplate.AreaDescription }} -

- } - -
- @for (i of [1, 2, 3, 4]; track i) { - @if (service.isNpcOrGoObj(i)) { -

- • {{ !!service.getObjText(i) ? service.getObjText(i) : (service.getObjective$(i) | async) }} - {{ service.getObjectiveCount(i) }} -

- } - } -
- -
- @for (i of [1, 2, 3, 4, 5, 6]; track i) { - @if (service.questTemplate['RequiredItemId' + i]; as reqItem) { -

- •  {{ - service.mysqlQueryService.getItemNameById(reqItem) | async - }} - {{ service.getObjItemCount(i) }} -

- } - } -
- -
- @for (i of [1, 2]; track i) { - @if (service.questTemplate['RequiredFactionId' + i]; as reqFaction) { -

- • {{ service.sqliteQueryService.getFactionNameById(reqFaction) | async }} - {{ service.getFactionByValue(i) }} -

- } - } -
-
+

Description diff --git a/libs/features/quest/src/quest-preview/quest-preview.component.ts b/libs/features/quest/src/quest-preview/quest-preview.component.ts index c405187bb72..c0c96a29677 100644 --- a/libs/features/quest/src/quest-preview/quest-preview.component.ts +++ b/libs/features/quest/src/quest-preview/quest-preview.component.ts @@ -1,6 +1,12 @@ import { AsyncPipe } from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit } from '@angular/core'; import { IconComponent } from '@keira/shared/base-editor-components'; +import { + FactionRequirement, + ItemObjective, + NpcOrGoObjective, + QuestObjectivesComponent, +} from './quest-objectives.component'; import { RacesTextKey, RacesTextValue } from '@keira/shared/constants'; import { PreviewHelperService } from '@keira/shared/preview'; import { CollapseModule } from 'ngx-bootstrap/collapse'; @@ -13,7 +19,7 @@ import { QuestPreviewService } from './quest-preview.service'; selector: 'keira-quest-preview', templateUrl: './quest-preview.component.html', styleUrls: ['./quest-preview.component.scss'], - imports: [IconComponent, CollapseModule, AsyncPipe], + imports: [IconComponent, CollapseModule, AsyncPipe, QuestObjectivesComponent], }) export class QuestPreviewComponent implements OnInit { private readonly changeDetectorRef = inject(ChangeDetectorRef); @@ -72,4 +78,45 @@ export class QuestPreviewComponent implements OnInit { hasPrevOrNext(questLists: { prev?: Quest[] | null; next?: Quest[] | null }): boolean { return !!(questLists.prev && questLists.prev.length > 0) || !!(questLists.next && questLists.next.length > 0); } + + get npcObjectivesData(): NpcOrGoObjective[] { + const array: NpcOrGoObjective[] = []; + for (let i = 1; i <= 4; i++) { + if (this.service.isNpcOrGoObj(i)) { + const text = this.service.getObjText(i); + const text$ = text ? Promise.resolve(text) : this.service.getObjective$(i); + array.push({ text$, count: this.service.getObjectiveCount(i) }); + } + } + return array; + } + + get itemObjectivesData(): ItemObjective[] { + const array: ItemObjective[] = []; + for (let i = 1; i <= 6; i++) { + const reqItem = this.service.questTemplate['RequiredItemId' + i]; + if (reqItem) { + array.push({ + id: reqItem, + name$: this.service.mysqlQueryService.getItemNameById(reqItem), + count: this.service.getObjItemCount(i), + }); + } + } + return array; + } + + get factionRequirementsData(): FactionRequirement[] { + const array: FactionRequirement[] = []; + for (let i = 1; i <= 2; i++) { + const reqFaction = this.service.questTemplate['RequiredFactionId' + i]; + if (reqFaction) { + array.push({ + name$: this.service.sqliteQueryService.getFactionNameById(reqFaction), + value: this.service.getFactionByValue(i), + }); + } + } + return array; + } } From 0df1c3beb93d25ee5135c61ce0155d81e8c10f32 Mon Sep 17 00:00:00 2001 From: ggro Date: Sun, 10 May 2026 00:36:26 -0300 Subject: [PATCH 2/5] my changes for pr-3370 --- .../quest-objectives.component.html | 2 -- .../quest-objectives.component.scss | 0 .../quest-objectives.component.ts | 10 +++--- .../quest-preview.component.html | 19 +++++++---- .../quest-preview.component.spec.ts | 7 ++++ .../quest-preview/quest-preview.component.ts | 32 +++++++++++++++---- 6 files changed, 49 insertions(+), 21 deletions(-) delete mode 100644 libs/features/quest/src/quest-preview/quest-objectives.component.scss diff --git a/libs/features/quest/src/quest-preview/quest-objectives.component.html b/libs/features/quest/src/quest-preview/quest-objectives.component.html index c1ff30b1d23..d5497183b82 100644 --- a/libs/features/quest/src/quest-preview/quest-objectives.component.html +++ b/libs/features/quest/src/quest-preview/quest-objectives.component.html @@ -1,6 +1,4 @@

-

Objectives

-
{{ objectiveText() }}
diff --git a/libs/features/quest/src/quest-preview/quest-objectives.component.scss b/libs/features/quest/src/quest-preview/quest-objectives.component.scss deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/libs/features/quest/src/quest-preview/quest-objectives.component.ts b/libs/features/quest/src/quest-preview/quest-objectives.component.ts index 2e967e06c39..3cbd25a179d 100644 --- a/libs/features/quest/src/quest-preview/quest-objectives.component.ts +++ b/libs/features/quest/src/quest-preview/quest-objectives.component.ts @@ -1,27 +1,27 @@ import { AsyncPipe } from '@angular/common'; import { ChangeDetectionStrategy, Component, input } from '@angular/core'; import { IconComponent } from '@keira/shared/base-editor-components'; +import { Observable } from 'rxjs'; export interface NpcOrGoObjective { - text$: Promise; + text$: Observable; count: string; } export interface ItemObjective { - id: number; - name$: Promise; + id: number | string; + name$: Observable; count: string; } export interface FactionRequirement { - name$: Promise; + name$: Observable; value: string; } @Component({ selector: 'keira-quest-objectives', templateUrl: './quest-objectives.component.html', - styleUrls: ['./quest-objectives.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [IconComponent, AsyncPipe], diff --git a/libs/features/quest/src/quest-preview/quest-preview.component.html b/libs/features/quest/src/quest-preview/quest-preview.component.html index 518c9085ce8..7f777f61132 100644 --- a/libs/features/quest/src/quest-preview/quest-preview.component.html +++ b/libs/features/quest/src/quest-preview/quest-preview.component.html @@ -214,13 +214,18 @@
{{ service.title }}
} - +

+ Objective +

+
+ +

Description diff --git a/libs/features/quest/src/quest-preview/quest-preview.component.spec.ts b/libs/features/quest/src/quest-preview/quest-preview.component.spec.ts index afe507405ca..06fe27827b5 100644 --- a/libs/features/quest/src/quest-preview/quest-preview.component.spec.ts +++ b/libs/features/quest/src/quest-preview/quest-preview.component.spec.ts @@ -90,6 +90,9 @@ class QuestPreviewComponentPage extends PageObject { get completionText(): HTMLDivElement { return this.query('#completion-text'); } + get objectiveToggle(): HTMLParagraphElement { + return this.query('#objective-toggle'); + } get descriptionToggle(): HTMLParagraphElement { return this.query('#description-toggle'); } @@ -451,10 +454,14 @@ describe('QuestPreviewComponent', () => { fixture.detectChanges(); + page.expectCollapsed(page.objectiveToggle); page.expectCollapsed(page.descriptionToggle); page.expectCollapsed(page.progressToggle); page.expectCollapsed(page.completionToggle); + page.clickElement(page.objectiveToggle); + page.expectNotCollapsed(page.objectiveToggle); + page.clickElement(page.descriptionToggle); page.expectNotCollapsed(page.descriptionToggle); diff --git a/libs/features/quest/src/quest-preview/quest-preview.component.ts b/libs/features/quest/src/quest-preview/quest-preview.component.ts index a547d16a022..5363c3af957 100644 --- a/libs/features/quest/src/quest-preview/quest-preview.component.ts +++ b/libs/features/quest/src/quest-preview/quest-preview.component.ts @@ -9,6 +9,7 @@ import { PreviewHelperService } from '@keira/shared/preview'; import { CollapseModule } from 'ngx-bootstrap/collapse'; import { Quest } from './quest-preview.model'; import { QuestPreviewService } from './quest-preview.service'; +import { from, Observable, of } from 'rxjs'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -23,6 +24,7 @@ export class QuestPreviewComponent implements OnInit { readonly service: QuestPreviewService = inject(QuestPreviewService); protected readonly helper: PreviewHelperService = inject(PreviewHelperService); + objectiveToggle = true; descriptionToggle = true; progressToggle = true; completionToggle = true; @@ -77,7 +79,10 @@ export class QuestPreviewComponent implements OnInit { this.service .valueChanges$(300) .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(() => this.changeDetectorRef.markForCheck()); + .subscribe(() => { + this.invalidateObjectivesCache(); + this.changeDetectorRef.markForCheck(); + }); } getRaceText(raceIndex: RacesTextKey): RacesTextValue | null { @@ -88,44 +93,57 @@ export class QuestPreviewComponent implements OnInit { return !!(questLists.prev && questLists.prev.length > 0) || !!(questLists.next && questLists.next.length > 0); } + private _npcObjectivesData: NpcOrGoObjective[] | null = null; + private _itemObjectivesData: ItemObjective[] | null = null; + private _factionRequirementsData: FactionRequirement[] | null = null; + + invalidateObjectivesCache(): void { + this._npcObjectivesData = null; + this._itemObjectivesData = null; + this._factionRequirementsData = null; + } + get npcObjectivesData(): NpcOrGoObjective[] { + if (this._npcObjectivesData) return this._npcObjectivesData; const array: NpcOrGoObjective[] = []; for (let i = 1; i <= 4; i++) { if (this.service.isNpcOrGoObj(i)) { const text = this.service.getObjText(i); - const text$ = text ? Promise.resolve(text) : this.service.getObjective$(i); + const text$: Observable = text ? of(text) : from(this.service.getObjective$(i)); array.push({ text$, count: this.service.getObjectiveCount(i) }); } } - return array; + return (this._npcObjectivesData = array); } get itemObjectivesData(): ItemObjective[] { + if (this._itemObjectivesData) return this._itemObjectivesData; const array: ItemObjective[] = []; for (let i = 1; i <= 6; i++) { const reqItem = this.service.questTemplate['RequiredItemId' + i]; if (reqItem) { array.push({ id: reqItem, - name$: this.service.mysqlQueryService.getItemNameById(reqItem), + name$: from(this.service.mysqlQueryService.getItemNameById(reqItem)), count: this.service.getObjItemCount(i), }); } } - return array; + return (this._itemObjectivesData = array); } get factionRequirementsData(): FactionRequirement[] { + if (this._factionRequirementsData) return this._factionRequirementsData; const array: FactionRequirement[] = []; for (let i = 1; i <= 2; i++) { const reqFaction = this.service.questTemplate['RequiredFactionId' + i]; if (reqFaction) { array.push({ - name$: this.service.sqliteQueryService.getFactionNameById(reqFaction), + name$: from(this.service.sqliteQueryService.getFactionNameById(reqFaction)), value: this.service.getFactionByValue(i), }); } } - return array; + return (this._factionRequirementsData = array); } } From a504a848f28498c63bb288eb4b0ecdfe3d48ae98 Mon Sep 17 00:00:00 2001 From: ggro Date: Mon, 11 May 2026 18:32:11 -0300 Subject: [PATCH 3/5] merge styles and replace $B with newlines --- apps/keira/src/app/scss/_quest-preview.scss | 34 ++++++++++++++++-- .../quest-preview.component.html | 8 ++--- .../quest-preview.component.scss | 36 ------------------- .../quest-preview/quest-preview.component.ts | 1 - .../quest-preview/quest-preview.service.ts | 12 ++++++- 5 files changed, 47 insertions(+), 44 deletions(-) delete mode 100644 libs/features/quest/src/quest-preview/quest-preview.component.scss diff --git a/apps/keira/src/app/scss/_quest-preview.scss b/apps/keira/src/app/scss/_quest-preview.scss index 1445365d991..7fa4693c908 100644 --- a/apps/keira/src/app/scss/_quest-preview.scss +++ b/apps/keira/src/app/scss/_quest-preview.scss @@ -1,5 +1,6 @@ .quest-preview { - .r0 { + .r0, + .red { color: #f00 !important; } .r1 { @@ -11,7 +12,36 @@ .r3 { color: #40bf40 !important; } - .r4 { + .r4, + .greyed { color: #808080 !important; } + + color: white; + padding: 0 20px; + white-space: pre-line; + + p { + margin-bottom: 0.3rem; + } + + .title { + margin-top: 10px; + margin-bottom: 6px; + text-align: center; + font-weight: bold; + } + + ol, + ul { + padding: 0 0 0 17px; + } + + .colored { + color: #ffd100; + } + + .text-sm { + font-size: 0.8rem; + } } diff --git a/libs/features/quest/src/quest-preview/quest-preview.component.html b/libs/features/quest/src/quest-preview/quest-preview.component.html index 7f777f61132..16ac15bb250 100644 --- a/libs/features/quest/src/quest-preview/quest-preview.component.html +++ b/libs/features/quest/src/quest-preview/quest-preview.component.html @@ -1,6 +1,6 @@

-
+
@if (!!service.title) {
{{ service.title }}
} @@ -231,21 +231,21 @@
{{ service.title }}
Description

- {{ service.questTemplate.QuestDescription }} + {{ service.descriptionText }}

Progress (CompletionText)

- {{ service.questRequestItems.CompletionText }} + {{ service.completionText }}

Completion (RewardText)

- {{ service.questOfferReward.RewardText }} + {{ service.rewardText }}
@if (service.isReward()) { diff --git a/libs/features/quest/src/quest-preview/quest-preview.component.scss b/libs/features/quest/src/quest-preview/quest-preview.component.scss deleted file mode 100644 index 30e307c5734..00000000000 --- a/libs/features/quest/src/quest-preview/quest-preview.component.scss +++ /dev/null @@ -1,36 +0,0 @@ -.preview-content { - color: white; - padding: 0 20px; - - p { - margin-bottom: 0.3rem; - } - - .title { - margin-top: 10px; - margin-bottom: 6px; - text-align: center; - font-weight: bold; - } - - ol, - ul { - padding: 0 0 0 17px; - } - - .colored { - color: #ffd100; - } - - .greyed { - color: #dbdbdb; - } - - .red { - color: #f00; - } - - .text-sm { - font-size: 0.8rem; - } -} diff --git a/libs/features/quest/src/quest-preview/quest-preview.component.ts b/libs/features/quest/src/quest-preview/quest-preview.component.ts index 5363c3af957..103fef261f0 100644 --- a/libs/features/quest/src/quest-preview/quest-preview.component.ts +++ b/libs/features/quest/src/quest-preview/quest-preview.component.ts @@ -15,7 +15,6 @@ import { from, Observable, of } from 'rxjs'; changeDetection: ChangeDetectionStrategy.OnPush, selector: 'keira-quest-preview', templateUrl: './quest-preview.component.html', - styleUrls: ['./quest-preview.component.scss'], imports: [IconComponent, CollapseModule, AsyncPipe, QuestObjectivesComponent, Model3DViewerComponent], }) export class QuestPreviewComponent implements OnInit { diff --git a/libs/features/quest/src/quest-preview/quest-preview.service.ts b/libs/features/quest/src/quest-preview/quest-preview.service.ts index cf83bf3b737..ad016e7d143 100644 --- a/libs/features/quest/src/quest-preview/quest-preview.service.ts +++ b/libs/features/quest/src/quest-preview/quest-preview.service.ts @@ -124,8 +124,18 @@ export class QuestPreviewService { return this.mysqlQueryService.getItemNameById(this.startItem); } get objectiveText(): string { - return this.questTemplate.LogDescription; + return this.questTemplate.LogDescription.replace(/\$B/g, '\n'); } + get descriptionText(): string { + return this.questTemplate.QuestDescription.replace(/\$B/g, '\n'); + } + get completionText(): string { + return this.questRequestItems.CompletionText.replace(/\$B/g, '\n'); + } + get rewardText(): string { + return this.questOfferReward.RewardText.replace(/\$B/g, '\n'); + } + get rewardMoney(): number { return this.questTemplate.RewardMoney; } From 3befb03e626ec7a5cd61226195f5371dcfa7d2f0 Mon Sep 17 00:00:00 2001 From: ggro Date: Mon, 11 May 2026 19:34:05 -0300 Subject: [PATCH 4/5] Add schema to sql editor autocomplete dinamically --- .../sql-editor/src/sql-editor.component.html | 4 +- .../sql-editor/src/sql-editor.component.ts | 23 ++---- .../sql-editor/src/sql-editor.service.ts | 74 ++++++++++++++++++- 3 files changed, 82 insertions(+), 19 deletions(-) diff --git a/libs/features/sql-editor/src/sql-editor.component.html b/libs/features/sql-editor/src/sql-editor.component.html index ed7c51e0424..6de2a08d7dd 100644 --- a/libs/features/sql-editor/src/sql-editor.component.html +++ b/libs/features/sql-editor/src/sql-editor.component.html @@ -15,10 +15,10 @@ 0) { const columns = Object.keys(rows[0]); - - if (columns.length > this.MAX_COL_SHOWN) { - this._columns = columns.slice(0, this.MAX_COL_SHOWN); - } else { - this._columns = columns; - } + this._columns = columns.length > this.MAX_COL_SHOWN ? columns.slice(0, this.MAX_COL_SHOWN) : columns; } else { this._columns = []; } diff --git a/libs/features/sql-editor/src/sql-editor.service.ts b/libs/features/sql-editor/src/sql-editor.service.ts index f2dfab3c8c9..11817ae60da 100644 --- a/libs/features/sql-editor/src/sql-editor.service.ts +++ b/libs/features/sql-editor/src/sql-editor.service.ts @@ -1,13 +1,85 @@ -import { Injectable } from '@angular/core'; +import { computed, inject, Injectable, signal } from '@angular/core'; +import { TableRow } from '@keira/shared/constants'; +import { MysqlQueryService } from '@keira/shared/db-layer'; +import { LanguageDescription } from '@codemirror/language'; +import { MySQL, sql } from '@codemirror/lang-sql'; +import { forkJoin, map, of, switchMap } from 'rxjs'; +import { githubLight } from '@uiw/codemirror-theme-github'; + +export type SqlSchema = Record; @Injectable({ providedIn: 'root', }) export class SqlEditorService { + private readonly mysqlQueryService = inject(MysqlQueryService); + code = 'SELECT `entry`, `name`, `subname`, `minlevel`, `maxlevel`, `AIName`, `ScriptName` \n' + 'FROM `creature_template` \n' + 'WHERE `entry` > 100 \n' + 'ORDER BY `entry` ASC \n' + 'LIMIT 100'; + + readonly schema = signal({}); + + readonly schemaLoading = signal(false); + + readonly languages = computed(() => [ + LanguageDescription.of({ + name: 'MySQL', + support: sql({ + dialect: MySQL, + upperCaseKeywords: true, + schema: this.schema(), + }), + }), + ]); + + readonly extensions = computed(() => [ + githubLight, + sql({ + dialect: MySQL, + upperCaseKeywords: true, + schema: this.schema(), + }), + ]); + + loadSchema(): void { + this.schemaLoading.set(true); + + this.mysqlQueryService + .getTables() + .pipe( + map((tableRows: TableRow[]) => { + return tableRows.map((row) => Object.values(row)[0] as string); + }), + switchMap((tableNames: string[]) => { + if (tableNames.length === 0) { + return of({} as SqlSchema); + } + return forkJoin( + Object.fromEntries( + tableNames.map((tableName) => [ + tableName, + this.mysqlQueryService.getColumns(tableName).pipe( + map((colRows: TableRow[]) => { + return colRows.map((col) => col['Field'] as string); + }), + ), + ]), + ), + ); + }), + ) + .subscribe({ + next: (schemaMap) => { + this.schema.set(schemaMap as SqlSchema); + this.schemaLoading.set(false); + }, + error: () => { + this.schemaLoading.set(false); + }, + }); + } } From ee5d81e88992f9b409e982322b93345e20a764f2 Mon Sep 17 00:00:00 2001 From: ggro Date: Mon, 11 May 2026 20:20:47 -0300 Subject: [PATCH 5/5] missing file from last commit, show/hide trace button on sql editor error component --- .../query-error/query-error.component.html | 10 +++++++++- .../query-output/query-error/query-error.component.ts | 1 + libs/shared/db-layer/src/query/mysql-query.service.ts | 8 ++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/libs/shared/base-editor-components/src/query-output/query-error/query-error.component.html b/libs/shared/base-editor-components/src/query-output/query-error/query-error.component.html index b553f9d48f0..394592c4a9c 100644 --- a/libs/shared/base-editor-components/src/query-output/query-error/query-error.component.html +++ b/libs/shared/base-editor-components/src/query-output/query-error/query-error.component.html @@ -3,7 +3,15 @@ - + + + + + diff --git a/libs/shared/base-editor-components/src/query-output/query-error/query-error.component.ts b/libs/shared/base-editor-components/src/query-output/query-error/query-error.component.ts index 3d5eb966974..54c99254e69 100644 --- a/libs/shared/base-editor-components/src/query-output/query-error/query-error.component.ts +++ b/libs/shared/base-editor-components/src/query-output/query-error/query-error.component.ts @@ -10,4 +10,5 @@ import { QueryError } from 'mysql2'; }) export class QueryErrorComponent { @Input() error: QueryError | undefined; + showTrace = false; } diff --git a/libs/shared/db-layer/src/query/mysql-query.service.ts b/libs/shared/db-layer/src/query/mysql-query.service.ts index 420c1934dfd..907fd303edf 100644 --- a/libs/shared/db-layer/src/query/mysql-query.service.ts +++ b/libs/shared/db-layer/src/query/mysql-query.service.ts @@ -462,4 +462,12 @@ export class MysqlQueryService extends BaseQueryService { `SELECT displayId AS v FROM gameobject_template WHERE entry=${gameObjectId}`, ); } + + getTables(): Observable { + return this.query('SHOW TABLES'); + } + + getColumns(tableName: string): Observable { + return this.query(`SHOW COLUMNS FROM \`${tableName}\``); + } }
Error:{{ error.stack }} + +
{{ showTrace ? error.stack : error.message }}
Code: