@zentto/report-designer 1.5.7 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data-panel/db-connector.d.ts +77 -0
- package/dist/data-panel/db-connector.d.ts.map +1 -0
- package/dist/data-panel/db-connector.js +584 -0
- package/dist/data-panel/db-connector.js.map +1 -0
- package/dist/zentto-report-designer.d.ts +10 -1
- package/dist/zentto-report-designer.d.ts.map +1 -1
- package/dist/zentto-report-designer.js +90 -14
- package/dist/zentto-report-designer.js.map +1 -1
- package/package.json +5 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { LitElement, nothing } from 'lit';
|
|
2
|
+
export interface DbConnectionConfig {
|
|
3
|
+
type: 'postgres' | 'sqlserver' | 'mysql' | 'sqlite';
|
|
4
|
+
host?: string;
|
|
5
|
+
port?: number;
|
|
6
|
+
database: string;
|
|
7
|
+
username?: string;
|
|
8
|
+
password?: string;
|
|
9
|
+
filePath?: string;
|
|
10
|
+
ssl?: boolean;
|
|
11
|
+
windowsAuth?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface DbTableInfo {
|
|
14
|
+
schema: string;
|
|
15
|
+
name: string;
|
|
16
|
+
type: 'table' | 'view';
|
|
17
|
+
}
|
|
18
|
+
export interface DbColumnInfo {
|
|
19
|
+
name: string;
|
|
20
|
+
type: string;
|
|
21
|
+
nullable: boolean;
|
|
22
|
+
isPrimaryKey: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface DbConnectorProvider {
|
|
25
|
+
testConnection(config: DbConnectionConfig): Promise<{
|
|
26
|
+
success: boolean;
|
|
27
|
+
error?: string;
|
|
28
|
+
}>;
|
|
29
|
+
getTables(config: DbConnectionConfig): Promise<DbTableInfo[]>;
|
|
30
|
+
getColumns(config: DbConnectionConfig, schema: string, table: string): Promise<DbColumnInfo[]>;
|
|
31
|
+
executeQuery(config: DbConnectionConfig, sql: string): Promise<{
|
|
32
|
+
rows: Record<string, unknown>[];
|
|
33
|
+
rowCount: number;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
export declare class DbConnector extends LitElement {
|
|
37
|
+
open: boolean;
|
|
38
|
+
provider: DbConnectorProvider | null;
|
|
39
|
+
private _dbType;
|
|
40
|
+
private _host;
|
|
41
|
+
private _port;
|
|
42
|
+
private _database;
|
|
43
|
+
private _username;
|
|
44
|
+
private _password;
|
|
45
|
+
private _ssl;
|
|
46
|
+
private _windowsAuth;
|
|
47
|
+
private _filePath;
|
|
48
|
+
private _testing;
|
|
49
|
+
private _testResult;
|
|
50
|
+
private _tables;
|
|
51
|
+
private _loadingTables;
|
|
52
|
+
private _selectedTable;
|
|
53
|
+
private _columns;
|
|
54
|
+
private _loadingColumns;
|
|
55
|
+
private _expandedSchemas;
|
|
56
|
+
private _selectedTables;
|
|
57
|
+
static styles: import("lit").CSSResult;
|
|
58
|
+
private _buildConfig;
|
|
59
|
+
private _testConnection;
|
|
60
|
+
private _exploreTables;
|
|
61
|
+
private _selectTable;
|
|
62
|
+
private _toggleTableSelection;
|
|
63
|
+
private _toggleSchema;
|
|
64
|
+
private _onConfirm;
|
|
65
|
+
private _onCancel;
|
|
66
|
+
private _onDbTypeChange;
|
|
67
|
+
private _renderForm;
|
|
68
|
+
private _renderTables;
|
|
69
|
+
private _renderColumns;
|
|
70
|
+
render(): import("lit-html").TemplateResult<1> | typeof nothing;
|
|
71
|
+
}
|
|
72
|
+
declare global {
|
|
73
|
+
interface HTMLElementTagNameMap {
|
|
74
|
+
'zrd-db-connector': DbConnector;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=db-connector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db-connector.d.ts","sourceRoot":"","sources":["../../src/data-panel/db-connector.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAa,OAAO,EAAE,MAAM,KAAK,CAAC;AAOrD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,OAAO,GAAG,QAAQ,CAAC;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1F,SAAS,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC9D,UAAU,CAAC,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAC/F,YAAY,CAAC,MAAM,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACvH;AAgCD,qBACa,WAAY,SAAQ,UAAU;IACZ,IAAI,UAAS;IACV,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAQ;IAEnE,OAAO,CAAC,OAAO,CAA0C;IACzD,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,SAAS,CAAM;IAEvB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAqD;IAExE,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,cAAc,CAAiD;IACvE,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,gBAAgB,CAAqB;IAE7C,OAAO,CAAC,eAAe,CAAqB;IAErD,OAAgB,MAAM,0BA6IpB;IAIF,OAAO,CAAC,YAAY;YAgBN,eAAe;YAYf,cAAc;YAcd,YAAY;IAY1B,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,aAAa;YAOP,UAAU;IA4CxB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,WAAW;IA6DnB,OAAO,CAAC,aAAa;IA2CrB,OAAO,CAAC,cAAc;IAkBb,MAAM;CA2DhB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,kBAAkB,EAAE,WAAW,CAAC;KACjC;CACF"}
|
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
// @zentto/report-designer — Database Connector Dialog (Lit web component)
|
|
2
|
+
// Ported from zentto-report-studio dialogs.ts — native to the npm package.
|
|
3
|
+
// Supports PostgreSQL, SQL Server, MySQL, SQLite via a DbConnectorProvider.
|
|
4
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
5
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
6
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
7
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
8
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
9
|
+
};
|
|
10
|
+
import { LitElement, html, css, nothing } from 'lit';
|
|
11
|
+
import { customElement, property, state } from 'lit/decorators.js';
|
|
12
|
+
// ─── Helpers ──────────────────────────────────────────────────────
|
|
13
|
+
const DB_TYPES = [
|
|
14
|
+
{ type: 'postgres', label: 'PostgreSQL', icon: '\u{1F418}', port: 5432, color: '#336791' },
|
|
15
|
+
{ type: 'sqlserver', label: 'SQL Server', icon: '\u{1F4CB}', port: 1433, color: '#cc2927' },
|
|
16
|
+
{ type: 'mysql', label: 'MySQL', icon: '\u{1F4E6}', port: 3306, color: '#4479a1' },
|
|
17
|
+
{ type: 'sqlite', label: 'SQLite', icon: '\u{1F4F1}', port: 0, color: '#003b57' },
|
|
18
|
+
];
|
|
19
|
+
function fieldTypeFromSql(colType) {
|
|
20
|
+
const t = (colType || '').toLowerCase();
|
|
21
|
+
if (/int|numeric|decimal|float|double|real|serial/.test(t))
|
|
22
|
+
return 'number';
|
|
23
|
+
if (/money|currency/.test(t))
|
|
24
|
+
return 'currency';
|
|
25
|
+
if (/date|time|timestamp/.test(t))
|
|
26
|
+
return 'date';
|
|
27
|
+
if (/bool|bit/.test(t))
|
|
28
|
+
return 'boolean';
|
|
29
|
+
return 'string';
|
|
30
|
+
}
|
|
31
|
+
function typeBadgeColor(colType) {
|
|
32
|
+
const t = (colType || '').toLowerCase();
|
|
33
|
+
if (/int|numeric|decimal|float|double|real|serial/.test(t))
|
|
34
|
+
return '#1976d2';
|
|
35
|
+
if (/money|currency/.test(t))
|
|
36
|
+
return '#ff9800';
|
|
37
|
+
if (/date|time|timestamp/.test(t))
|
|
38
|
+
return '#e91e63';
|
|
39
|
+
if (/bool|bit/.test(t))
|
|
40
|
+
return '#9c27b0';
|
|
41
|
+
if (/varchar|char|text|nvarchar|nchar/.test(t))
|
|
42
|
+
return '#4caf50';
|
|
43
|
+
return '#757575';
|
|
44
|
+
}
|
|
45
|
+
// ─── Component ────────────────────────────────────────────────────
|
|
46
|
+
let DbConnector = class DbConnector extends LitElement {
|
|
47
|
+
constructor() {
|
|
48
|
+
super(...arguments);
|
|
49
|
+
this.open = false;
|
|
50
|
+
this.provider = null;
|
|
51
|
+
this._dbType = 'postgres';
|
|
52
|
+
this._host = 'localhost';
|
|
53
|
+
this._port = 5432;
|
|
54
|
+
this._database = '';
|
|
55
|
+
this._username = '';
|
|
56
|
+
this._password = '';
|
|
57
|
+
this._ssl = false;
|
|
58
|
+
this._windowsAuth = false;
|
|
59
|
+
this._filePath = '';
|
|
60
|
+
this._testing = false;
|
|
61
|
+
this._testResult = null;
|
|
62
|
+
this._tables = [];
|
|
63
|
+
this._loadingTables = false;
|
|
64
|
+
this._selectedTable = null;
|
|
65
|
+
this._columns = [];
|
|
66
|
+
this._loadingColumns = false;
|
|
67
|
+
this._expandedSchemas = new Set();
|
|
68
|
+
this._selectedTables = new Set(); // "schema.table" keys
|
|
69
|
+
}
|
|
70
|
+
static { this.styles = css `
|
|
71
|
+
:host {
|
|
72
|
+
display: flex; flex-direction: column;
|
|
73
|
+
font-family: 'Segoe UI', Roboto, Arial, sans-serif;
|
|
74
|
+
font-size: 12px; height: 100%; overflow: hidden;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.panel {
|
|
78
|
+
display: flex; flex-direction: column; height: 100%; overflow-y: auto;
|
|
79
|
+
padding: 8px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.panel-header {
|
|
83
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
84
|
+
padding-bottom: 8px; border-bottom: 1px solid var(--zrd-border, #eee);
|
|
85
|
+
margin-bottom: 8px;
|
|
86
|
+
}
|
|
87
|
+
.panel-title { font-size: 12px; font-weight: 600; }
|
|
88
|
+
.back-btn {
|
|
89
|
+
background: none; border: none; font-size: 11px;
|
|
90
|
+
cursor: pointer; color: var(--zrd-primary, #1976d2);
|
|
91
|
+
padding: 3px 8px; border-radius: 4px;
|
|
92
|
+
}
|
|
93
|
+
.back-btn:hover { background: var(--zrd-hover, #f0f7ff); }
|
|
94
|
+
|
|
95
|
+
.actions-bar {
|
|
96
|
+
display: flex; gap: 4px; flex-wrap: wrap; margin-top: 8px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* ─── DB Type Grid ────────────────────────── */
|
|
100
|
+
.db-type-grid {
|
|
101
|
+
display: grid; grid-template-columns: 1fr 1fr;
|
|
102
|
+
gap: 8px; margin-bottom: 20px;
|
|
103
|
+
}
|
|
104
|
+
.db-type-btn {
|
|
105
|
+
padding: 14px 8px; border: 2px solid var(--zrd-border, #eee);
|
|
106
|
+
border-radius: 8px; cursor: pointer; text-align: center;
|
|
107
|
+
background: var(--zrd-panel-bg, #fff); transition: all 0.15s;
|
|
108
|
+
}
|
|
109
|
+
.db-type-btn:hover { border-color: #1976d2; background: #f0f7ff; }
|
|
110
|
+
.db-type-btn.active { border-color: #1976d2; background: #e3f2fd; }
|
|
111
|
+
.db-type-icon { font-size: 28px; display: block; margin-bottom: 4px; }
|
|
112
|
+
.db-type-label { font-weight: 600; font-size: 13px; }
|
|
113
|
+
.db-type-port { font-size: 11px; color: var(--zrd-text-muted, #999); }
|
|
114
|
+
|
|
115
|
+
/* ─── Form ────────────────────────────────── */
|
|
116
|
+
.form-row { margin-bottom: 12px; }
|
|
117
|
+
.form-row-2col { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
|
118
|
+
.form-label {
|
|
119
|
+
display: block; font-size: 12px; font-weight: 600;
|
|
120
|
+
color: var(--zrd-text, #333); margin-bottom: 4px;
|
|
121
|
+
}
|
|
122
|
+
.form-input {
|
|
123
|
+
width: 100%; padding: 8px 12px;
|
|
124
|
+
border: 1px solid var(--zrd-border, #ddd); border-radius: 6px;
|
|
125
|
+
font-size: 13px; box-sizing: border-box;
|
|
126
|
+
background: var(--zrd-input-bg, #fff); color: var(--zrd-text, #333);
|
|
127
|
+
}
|
|
128
|
+
.form-input:focus { outline: none; border-color: #1976d2; }
|
|
129
|
+
|
|
130
|
+
.checkbox-row {
|
|
131
|
+
display: flex; align-items: center; gap: 6px;
|
|
132
|
+
font-size: 12px; color: var(--zrd-text, #555); margin-bottom: 8px;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* ─── Test Result ─────────────────────────── */
|
|
136
|
+
.test-result {
|
|
137
|
+
padding: 8px 12px; border-radius: 6px;
|
|
138
|
+
margin-top: 12px; font-size: 12px; display: flex; align-items: center; gap: 6px;
|
|
139
|
+
}
|
|
140
|
+
.test-result.success { background: #e8f5e9; color: #2e7d32; }
|
|
141
|
+
.test-result.error { background: #ffebee; color: #c62828; }
|
|
142
|
+
|
|
143
|
+
/* ─── Tables Browser ──────────────────────── */
|
|
144
|
+
.tables-section {
|
|
145
|
+
margin-top: 16px; border: 1px solid var(--zrd-border, #eee);
|
|
146
|
+
border-radius: 8px; max-height: 320px; overflow-y: auto;
|
|
147
|
+
}
|
|
148
|
+
.schema-header {
|
|
149
|
+
padding: 8px 12px; font-weight: 600; font-size: 12px;
|
|
150
|
+
background: var(--zrd-hover, #f9f9f9); cursor: pointer;
|
|
151
|
+
display: flex; align-items: center; gap: 6px;
|
|
152
|
+
border-bottom: 1px solid var(--zrd-border, #eee);
|
|
153
|
+
}
|
|
154
|
+
.schema-header:hover { background: #eef3f8; }
|
|
155
|
+
.schema-chevron { font-size: 10px; transition: transform 0.15s; }
|
|
156
|
+
.schema-chevron.open { transform: rotate(90deg); }
|
|
157
|
+
|
|
158
|
+
.table-row {
|
|
159
|
+
padding: 6px 12px 6px 28px;
|
|
160
|
+
display: flex; align-items: center; gap: 8px;
|
|
161
|
+
cursor: pointer; font-size: 12px;
|
|
162
|
+
border-bottom: 1px solid var(--zrd-border, #f5f5f5);
|
|
163
|
+
}
|
|
164
|
+
.table-row:hover { background: #f0f7ff; }
|
|
165
|
+
.table-row.selected { background: #e3f2fd; }
|
|
166
|
+
.table-icon { font-size: 14px; }
|
|
167
|
+
.table-name { flex: 1; font-weight: 500; }
|
|
168
|
+
.table-type { font-size: 10px; color: var(--zrd-text-muted, #999); }
|
|
169
|
+
.table-check { width: 16px; height: 16px; accent-color: #1976d2; }
|
|
170
|
+
|
|
171
|
+
/* ─── Columns List ────────────────────────── */
|
|
172
|
+
.columns-section {
|
|
173
|
+
margin-top: 12px; border: 1px solid var(--zrd-border, #eee);
|
|
174
|
+
border-radius: 8px; padding: 8px 0;
|
|
175
|
+
}
|
|
176
|
+
.columns-title {
|
|
177
|
+
padding: 4px 12px; font-weight: 600; font-size: 11px;
|
|
178
|
+
color: var(--zrd-text-muted, #888); text-transform: uppercase; letter-spacing: 0.5px;
|
|
179
|
+
}
|
|
180
|
+
.column-row {
|
|
181
|
+
padding: 4px 12px; display: flex; align-items: center; gap: 6px; font-size: 12px;
|
|
182
|
+
}
|
|
183
|
+
.column-row:hover { background: var(--zrd-hover, #f9f9f9); }
|
|
184
|
+
.col-pk { color: #ff9800; font-size: 11px; }
|
|
185
|
+
.col-name { flex: 1; font-weight: 500; }
|
|
186
|
+
.col-type {
|
|
187
|
+
font-size: 10px; padding: 1px 6px; border-radius: 3px;
|
|
188
|
+
color: #fff; font-weight: 600;
|
|
189
|
+
}
|
|
190
|
+
.col-nullable { font-size: 10px; color: var(--zrd-text-muted, #aaa); }
|
|
191
|
+
|
|
192
|
+
/* ─── Buttons ─────────────────────────────── */
|
|
193
|
+
.btn {
|
|
194
|
+
padding: 8px 20px; border-radius: 6px; border: 1px solid var(--zrd-border, #ddd);
|
|
195
|
+
cursor: pointer; font-size: 13px; font-weight: 500;
|
|
196
|
+
background: var(--zrd-panel-bg, #fff); color: var(--zrd-text, #333);
|
|
197
|
+
}
|
|
198
|
+
.btn:hover { background: #f5f5f5; }
|
|
199
|
+
.btn:disabled { opacity: 0.5; cursor: default; }
|
|
200
|
+
.btn-primary { background: #1976d2; color: #fff; border-color: #1976d2; }
|
|
201
|
+
.btn-primary:hover { background: #1565c0; }
|
|
202
|
+
.btn-success { background: #2e7d32; color: #fff; border-color: #2e7d32; }
|
|
203
|
+
.btn-success:hover { background: #1b5e20; }
|
|
204
|
+
|
|
205
|
+
.spinner {
|
|
206
|
+
display: inline-block; width: 14px; height: 14px;
|
|
207
|
+
border: 2px solid #ddd; border-top-color: #1976d2;
|
|
208
|
+
border-radius: 50%; animation: spin 0.6s linear infinite;
|
|
209
|
+
}
|
|
210
|
+
@keyframes spin { to { transform: rotate(360deg); } }
|
|
211
|
+
`; }
|
|
212
|
+
// ─── Connection Config Builder ────────────────────────────────
|
|
213
|
+
_buildConfig() {
|
|
214
|
+
return {
|
|
215
|
+
type: this._dbType,
|
|
216
|
+
host: this._dbType === 'sqlite' ? undefined : this._host,
|
|
217
|
+
port: this._dbType === 'sqlite' ? undefined : this._port,
|
|
218
|
+
database: this._dbType === 'sqlite' ? this._filePath : this._database,
|
|
219
|
+
username: this._dbType === 'sqlite' ? undefined : this._username,
|
|
220
|
+
password: this._dbType === 'sqlite' ? undefined : this._password,
|
|
221
|
+
filePath: this._dbType === 'sqlite' ? this._filePath : undefined,
|
|
222
|
+
ssl: this._dbType === 'postgres' ? this._ssl : undefined,
|
|
223
|
+
windowsAuth: this._dbType === 'sqlserver' ? this._windowsAuth : undefined,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
// ─── Actions ──────────────────────────────────────────────────
|
|
227
|
+
async _testConnection() {
|
|
228
|
+
if (!this.provider)
|
|
229
|
+
return;
|
|
230
|
+
this._testing = true;
|
|
231
|
+
this._testResult = null;
|
|
232
|
+
try {
|
|
233
|
+
this._testResult = await this.provider.testConnection(this._buildConfig());
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
this._testResult = { success: false, error: err?.message || 'Connection failed' };
|
|
237
|
+
}
|
|
238
|
+
this._testing = false;
|
|
239
|
+
}
|
|
240
|
+
async _exploreTables() {
|
|
241
|
+
if (!this.provider)
|
|
242
|
+
return;
|
|
243
|
+
this._loadingTables = true;
|
|
244
|
+
try {
|
|
245
|
+
this._tables = await this.provider.getTables(this._buildConfig());
|
|
246
|
+
// Auto-expand all schemas
|
|
247
|
+
const schemas = new Set(this._tables.map(t => t.schema));
|
|
248
|
+
this._expandedSchemas = schemas;
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
this._testResult = { success: false, error: `Failed to load tables: ${err?.message}` };
|
|
252
|
+
}
|
|
253
|
+
this._loadingTables = false;
|
|
254
|
+
}
|
|
255
|
+
async _selectTable(schema, name) {
|
|
256
|
+
if (!this.provider)
|
|
257
|
+
return;
|
|
258
|
+
this._selectedTable = { schema, name };
|
|
259
|
+
this._loadingColumns = true;
|
|
260
|
+
try {
|
|
261
|
+
this._columns = await this.provider.getColumns(this._buildConfig(), schema, name);
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
this._columns = [];
|
|
265
|
+
}
|
|
266
|
+
this._loadingColumns = false;
|
|
267
|
+
}
|
|
268
|
+
_toggleTableSelection(schema, name) {
|
|
269
|
+
const key = `${schema}.${name}`;
|
|
270
|
+
const selected = new Set(this._selectedTables);
|
|
271
|
+
if (selected.has(key)) {
|
|
272
|
+
selected.delete(key);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
selected.add(key);
|
|
276
|
+
}
|
|
277
|
+
this._selectedTables = selected;
|
|
278
|
+
}
|
|
279
|
+
_toggleSchema(schema) {
|
|
280
|
+
const expanded = new Set(this._expandedSchemas);
|
|
281
|
+
if (expanded.has(schema))
|
|
282
|
+
expanded.delete(schema);
|
|
283
|
+
else
|
|
284
|
+
expanded.add(schema);
|
|
285
|
+
this._expandedSchemas = expanded;
|
|
286
|
+
}
|
|
287
|
+
async _onConfirm() {
|
|
288
|
+
if (!this.provider || this._selectedTables.size === 0)
|
|
289
|
+
return;
|
|
290
|
+
const dataSources = [];
|
|
291
|
+
const sampleData = {};
|
|
292
|
+
for (const key of this._selectedTables) {
|
|
293
|
+
const [schema, table] = key.split('.');
|
|
294
|
+
try {
|
|
295
|
+
const cols = await this.provider.getColumns(this._buildConfig(), schema, table);
|
|
296
|
+
const dsId = `${schema}_${table}`;
|
|
297
|
+
dataSources.push({
|
|
298
|
+
id: dsId,
|
|
299
|
+
name: `${schema}.${table}`,
|
|
300
|
+
type: 'array',
|
|
301
|
+
schema,
|
|
302
|
+
table,
|
|
303
|
+
fields: cols.map(c => ({
|
|
304
|
+
name: c.name,
|
|
305
|
+
label: c.name,
|
|
306
|
+
type: fieldTypeFromSql(c.type),
|
|
307
|
+
isPrimaryKey: c.isPrimaryKey,
|
|
308
|
+
nullable: c.nullable,
|
|
309
|
+
nativeType: c.type,
|
|
310
|
+
})),
|
|
311
|
+
});
|
|
312
|
+
// Fetch sample data
|
|
313
|
+
try {
|
|
314
|
+
const result = await this.provider.executeQuery(this._buildConfig(), `SELECT * FROM "${schema}"."${table}" LIMIT 10`);
|
|
315
|
+
sampleData[dsId] = result.rows;
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
sampleData[dsId] = [];
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
catch { /* skip failed tables */ }
|
|
322
|
+
}
|
|
323
|
+
this.dispatchEvent(new CustomEvent('datasources-ready', {
|
|
324
|
+
detail: { dataSources, sampleData },
|
|
325
|
+
bubbles: true, composed: true,
|
|
326
|
+
}));
|
|
327
|
+
}
|
|
328
|
+
_onCancel() {
|
|
329
|
+
this.dispatchEvent(new CustomEvent('connector-close', { bubbles: true, composed: true }));
|
|
330
|
+
}
|
|
331
|
+
_onDbTypeChange(type) {
|
|
332
|
+
this._dbType = type;
|
|
333
|
+
const dbType = DB_TYPES.find(d => d.type === type);
|
|
334
|
+
if (dbType && dbType.port > 0)
|
|
335
|
+
this._port = dbType.port;
|
|
336
|
+
this._testResult = null;
|
|
337
|
+
this._tables = [];
|
|
338
|
+
this._columns = [];
|
|
339
|
+
this._selectedTable = null;
|
|
340
|
+
}
|
|
341
|
+
// ─── Rendering ────────────────────────────────────────────────
|
|
342
|
+
_renderForm() {
|
|
343
|
+
if (this._dbType === 'sqlite') {
|
|
344
|
+
return html `
|
|
345
|
+
<div class="form-row">
|
|
346
|
+
<label class="form-label">Database File</label>
|
|
347
|
+
<input class="form-input" type="text" placeholder="/path/to/database.db"
|
|
348
|
+
.value=${this._filePath}
|
|
349
|
+
@input=${(e) => { this._filePath = e.target.value; }} />
|
|
350
|
+
</div>
|
|
351
|
+
`;
|
|
352
|
+
}
|
|
353
|
+
return html `
|
|
354
|
+
<div class="form-row form-row-2col">
|
|
355
|
+
<div>
|
|
356
|
+
<label class="form-label">Host</label>
|
|
357
|
+
<input class="form-input" .value=${this._host}
|
|
358
|
+
@input=${(e) => { this._host = e.target.value; }} />
|
|
359
|
+
</div>
|
|
360
|
+
<div>
|
|
361
|
+
<label class="form-label">Puerto</label>
|
|
362
|
+
<input class="form-input" type="number" .value=${String(this._port)}
|
|
363
|
+
@input=${(e) => { this._port = parseInt(e.target.value) || 0; }} />
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
366
|
+
<div class="form-row">
|
|
367
|
+
<label class="form-label">Base de Datos</label>
|
|
368
|
+
<input class="form-input" .value=${this._database}
|
|
369
|
+
@input=${(e) => { this._database = e.target.value; }} />
|
|
370
|
+
</div>
|
|
371
|
+
${this._dbType === 'sqlserver' ? html `
|
|
372
|
+
<label class="checkbox-row">
|
|
373
|
+
<input type="checkbox" .checked=${this._windowsAuth}
|
|
374
|
+
@change=${(e) => { this._windowsAuth = e.target.checked; }} />
|
|
375
|
+
Windows Authentication
|
|
376
|
+
</label>
|
|
377
|
+
` : nothing}
|
|
378
|
+
${!this._windowsAuth ? html `
|
|
379
|
+
<div class="form-row form-row-2col">
|
|
380
|
+
<div>
|
|
381
|
+
<label class="form-label">Usuario</label>
|
|
382
|
+
<input class="form-input" .value=${this._username}
|
|
383
|
+
@input=${(e) => { this._username = e.target.value; }} />
|
|
384
|
+
</div>
|
|
385
|
+
<div>
|
|
386
|
+
<label class="form-label">Contrasena</label>
|
|
387
|
+
<input class="form-input" type="password" .value=${this._password}
|
|
388
|
+
@input=${(e) => { this._password = e.target.value; }} />
|
|
389
|
+
</div>
|
|
390
|
+
</div>
|
|
391
|
+
` : nothing}
|
|
392
|
+
${this._dbType === 'postgres' ? html `
|
|
393
|
+
<label class="checkbox-row">
|
|
394
|
+
<input type="checkbox" .checked=${this._ssl}
|
|
395
|
+
@change=${(e) => { this._ssl = e.target.checked; }} />
|
|
396
|
+
SSL
|
|
397
|
+
</label>
|
|
398
|
+
` : nothing}
|
|
399
|
+
`;
|
|
400
|
+
}
|
|
401
|
+
_renderTables() {
|
|
402
|
+
if (this._tables.length === 0)
|
|
403
|
+
return nothing;
|
|
404
|
+
// Group by schema
|
|
405
|
+
const bySchema = new Map();
|
|
406
|
+
for (const t of this._tables) {
|
|
407
|
+
if (!bySchema.has(t.schema))
|
|
408
|
+
bySchema.set(t.schema, []);
|
|
409
|
+
bySchema.get(t.schema).push(t);
|
|
410
|
+
}
|
|
411
|
+
return html `
|
|
412
|
+
<div class="tables-section">
|
|
413
|
+
${[...bySchema.entries()].map(([schema, tables]) => {
|
|
414
|
+
const isOpen = this._expandedSchemas.has(schema);
|
|
415
|
+
return html `
|
|
416
|
+
<div class="schema-header" @click=${() => this._toggleSchema(schema)}>
|
|
417
|
+
<span class="schema-chevron ${isOpen ? 'open' : ''}">\u25B6</span>
|
|
418
|
+
\u{1F4C1} ${schema}
|
|
419
|
+
<span style="color:var(--zrd-text-muted,#999);font-weight:normal;font-size:11px">(${tables.length})</span>
|
|
420
|
+
</div>
|
|
421
|
+
${isOpen ? tables.map(t => {
|
|
422
|
+
const key = `${t.schema}.${t.name}`;
|
|
423
|
+
const isSelected = this._selectedTable?.schema === t.schema && this._selectedTable?.name === t.name;
|
|
424
|
+
const isChecked = this._selectedTables.has(key);
|
|
425
|
+
return html `
|
|
426
|
+
<div class="table-row ${isSelected ? 'selected' : ''}"
|
|
427
|
+
@click=${() => this._selectTable(t.schema, t.name)}>
|
|
428
|
+
<input type="checkbox" class="table-check"
|
|
429
|
+
.checked=${isChecked}
|
|
430
|
+
@click=${(e) => { e.stopPropagation(); this._toggleTableSelection(t.schema, t.name); }}
|
|
431
|
+
@change=${(e) => { e.stopPropagation(); this._toggleTableSelection(t.schema, t.name); }} />
|
|
432
|
+
<span class="table-icon">${t.type === 'view' ? '\u{1F441}' : '\u{1F4CB}'}</span>
|
|
433
|
+
<span class="table-name">${t.name}</span>
|
|
434
|
+
<span class="table-type">${t.type}</span>
|
|
435
|
+
</div>
|
|
436
|
+
`;
|
|
437
|
+
}) : nothing}
|
|
438
|
+
`;
|
|
439
|
+
})}
|
|
440
|
+
</div>
|
|
441
|
+
`;
|
|
442
|
+
}
|
|
443
|
+
_renderColumns() {
|
|
444
|
+
if (!this._selectedTable || this._columns.length === 0)
|
|
445
|
+
return nothing;
|
|
446
|
+
return html `
|
|
447
|
+
<div class="columns-section">
|
|
448
|
+
<div class="columns-title">${this._selectedTable.schema}.${this._selectedTable.name} — ${this._columns.length} columns</div>
|
|
449
|
+
${this._columns.map(c => html `
|
|
450
|
+
<div class="column-row">
|
|
451
|
+
${c.isPrimaryKey ? html `<span class="col-pk">\u{1F511}</span>` : html `<span style="width:15px"></span>`}
|
|
452
|
+
<span class="col-name">${c.name}</span>
|
|
453
|
+
<span class="col-type" style="background:${typeBadgeColor(c.type)}">${c.type}</span>
|
|
454
|
+
${c.nullable ? html `<span class="col-nullable">?</span>` : nothing}
|
|
455
|
+
</div>
|
|
456
|
+
`)}
|
|
457
|
+
</div>
|
|
458
|
+
`;
|
|
459
|
+
}
|
|
460
|
+
render() {
|
|
461
|
+
if (!this.open)
|
|
462
|
+
return nothing;
|
|
463
|
+
return html `
|
|
464
|
+
<div class="panel">
|
|
465
|
+
<div class="panel-header">
|
|
466
|
+
<span class="panel-title">\u{1F5C4} Conexion a BD</span>
|
|
467
|
+
<button class="back-btn" @click=${this._onCancel}>\u2190 Volver</button>
|
|
468
|
+
</div>
|
|
469
|
+
|
|
470
|
+
<!-- DB Type Grid -->
|
|
471
|
+
<div class="db-type-grid">
|
|
472
|
+
${DB_TYPES.map(db => html `
|
|
473
|
+
<div class="db-type-btn ${this._dbType === db.type ? 'active' : ''}"
|
|
474
|
+
@click=${() => this._onDbTypeChange(db.type)}>
|
|
475
|
+
<span class="db-type-icon">${db.icon}</span>
|
|
476
|
+
<span class="db-type-label" style="color:${db.color}">${db.label}</span>
|
|
477
|
+
<span class="db-type-port">${db.port > 0 ? `${db.port}` : 'local'}</span>
|
|
478
|
+
</div>
|
|
479
|
+
`)}
|
|
480
|
+
</div>
|
|
481
|
+
|
|
482
|
+
<!-- Connection Form -->
|
|
483
|
+
${this._renderForm()}
|
|
484
|
+
|
|
485
|
+
<!-- Action Buttons -->
|
|
486
|
+
<div class="actions-bar">
|
|
487
|
+
<button class="btn btn-primary"
|
|
488
|
+
?disabled=${this._testing || !this.provider}
|
|
489
|
+
@click=${this._testConnection}>
|
|
490
|
+
${this._testing ? html `<span class="spinner"></span>` : nothing}
|
|
491
|
+
Probar
|
|
492
|
+
</button>
|
|
493
|
+
<button class="btn btn-primary"
|
|
494
|
+
?disabled=${!this._testResult?.success || this._loadingTables}
|
|
495
|
+
@click=${this._exploreTables}>
|
|
496
|
+
Explorar
|
|
497
|
+
</button>
|
|
498
|
+
${this._selectedTables.size > 0 ? html `
|
|
499
|
+
<button class="btn btn-success" @click=${this._onConfirm}>
|
|
500
|
+
\u2713 Usar ${this._selectedTables.size}
|
|
501
|
+
</button>
|
|
502
|
+
` : nothing}
|
|
503
|
+
</div>
|
|
504
|
+
|
|
505
|
+
<!-- Test Result -->
|
|
506
|
+
${this._testResult ? html `
|
|
507
|
+
<div class="test-result ${this._testResult.success ? 'success' : 'error'}">
|
|
508
|
+
${this._testResult.success ? '\u2705 Conexion exitosa' : `\u274C ${this._testResult.error}`}
|
|
509
|
+
</div>
|
|
510
|
+
` : nothing}
|
|
511
|
+
|
|
512
|
+
<!-- Tables -->
|
|
513
|
+
${this._loadingTables ? html `<div style="text-align:center;padding:12px"><span class="spinner"></span> Cargando...</div>` : nothing}
|
|
514
|
+
${this._renderTables()}
|
|
515
|
+
${this._loadingColumns ? html `<div style="text-align:center;padding:8px"><span class="spinner"></span></div>` : this._renderColumns()}
|
|
516
|
+
</div>
|
|
517
|
+
`;
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
__decorate([
|
|
521
|
+
property({ type: Boolean })
|
|
522
|
+
], DbConnector.prototype, "open", void 0);
|
|
523
|
+
__decorate([
|
|
524
|
+
property({ attribute: false })
|
|
525
|
+
], DbConnector.prototype, "provider", void 0);
|
|
526
|
+
__decorate([
|
|
527
|
+
state()
|
|
528
|
+
], DbConnector.prototype, "_dbType", void 0);
|
|
529
|
+
__decorate([
|
|
530
|
+
state()
|
|
531
|
+
], DbConnector.prototype, "_host", void 0);
|
|
532
|
+
__decorate([
|
|
533
|
+
state()
|
|
534
|
+
], DbConnector.prototype, "_port", void 0);
|
|
535
|
+
__decorate([
|
|
536
|
+
state()
|
|
537
|
+
], DbConnector.prototype, "_database", void 0);
|
|
538
|
+
__decorate([
|
|
539
|
+
state()
|
|
540
|
+
], DbConnector.prototype, "_username", void 0);
|
|
541
|
+
__decorate([
|
|
542
|
+
state()
|
|
543
|
+
], DbConnector.prototype, "_password", void 0);
|
|
544
|
+
__decorate([
|
|
545
|
+
state()
|
|
546
|
+
], DbConnector.prototype, "_ssl", void 0);
|
|
547
|
+
__decorate([
|
|
548
|
+
state()
|
|
549
|
+
], DbConnector.prototype, "_windowsAuth", void 0);
|
|
550
|
+
__decorate([
|
|
551
|
+
state()
|
|
552
|
+
], DbConnector.prototype, "_filePath", void 0);
|
|
553
|
+
__decorate([
|
|
554
|
+
state()
|
|
555
|
+
], DbConnector.prototype, "_testing", void 0);
|
|
556
|
+
__decorate([
|
|
557
|
+
state()
|
|
558
|
+
], DbConnector.prototype, "_testResult", void 0);
|
|
559
|
+
__decorate([
|
|
560
|
+
state()
|
|
561
|
+
], DbConnector.prototype, "_tables", void 0);
|
|
562
|
+
__decorate([
|
|
563
|
+
state()
|
|
564
|
+
], DbConnector.prototype, "_loadingTables", void 0);
|
|
565
|
+
__decorate([
|
|
566
|
+
state()
|
|
567
|
+
], DbConnector.prototype, "_selectedTable", void 0);
|
|
568
|
+
__decorate([
|
|
569
|
+
state()
|
|
570
|
+
], DbConnector.prototype, "_columns", void 0);
|
|
571
|
+
__decorate([
|
|
572
|
+
state()
|
|
573
|
+
], DbConnector.prototype, "_loadingColumns", void 0);
|
|
574
|
+
__decorate([
|
|
575
|
+
state()
|
|
576
|
+
], DbConnector.prototype, "_expandedSchemas", void 0);
|
|
577
|
+
__decorate([
|
|
578
|
+
state()
|
|
579
|
+
], DbConnector.prototype, "_selectedTables", void 0);
|
|
580
|
+
DbConnector = __decorate([
|
|
581
|
+
customElement('zrd-db-connector')
|
|
582
|
+
], DbConnector);
|
|
583
|
+
export { DbConnector };
|
|
584
|
+
//# sourceMappingURL=db-connector.js.map
|