deepspotscreen-sdk 0.1.2 → 0.2.0

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.
@@ -1,2272 +0,0 @@
1
- class ae {
2
- constructor(e, t) {
3
- this.tokenCache = /* @__PURE__ */ new Map(), this.baseUrl = e.replace(/\/$/, ""), this.apiKey = t;
4
- }
5
- // ── Embed Token ─────────────────────────────────────────────────────────────
6
- async getEmbedToken(e) {
7
- var i, a, o, c;
8
- const t = [
9
- e.dashboardId,
10
- e.embedType,
11
- (i = e.embedLevel) != null ? i : "",
12
- (a = e.componentId) != null ? a : "",
13
- (o = e.userId) != null ? o : ""
14
- ].join(":"), s = this.tokenCache.get(t);
15
- if (s && s.expiresAt > Date.now() + 6e4) return s.token;
16
- const r = await this.post(
17
- "/dashboard-builder/embed-token",
18
- {
19
- dashboardId: e.dashboardId,
20
- embedType: e.embedType,
21
- embedLevel: (c = e.embedLevel) != null ? c : "dashboard",
22
- componentId: e.componentId,
23
- userId: e.userId,
24
- tenantId: e.tenantId,
25
- expiresIn: 3600
26
- },
27
- { "x-deepspot-api-key": this.apiKey }
28
- );
29
- return this.tokenCache.set(t, {
30
- token: r.token,
31
- expiresAt: new Date(r.expiresAt).getTime()
32
- }), r.token;
33
- }
34
- // ── Dashboard Render (lazy — loads one tab at a time) ──────────────────────
35
- async getDashboardRender(e, t, s = {}) {
36
- var i;
37
- const r = new URLSearchParams({
38
- embedType: "dashboard",
39
- embedLevel: (i = s.embedLevel) != null ? i : "dashboard"
40
- });
41
- return s.pageId && r.set("pageId", s.pageId), s.tabId && r.set("tabId", s.tabId), s.filters && Object.entries(s.filters).forEach(([a, o]) => {
42
- o != null && o !== "" && r.set(`filter[${a}]`, Array.isArray(o) ? o.join(",") : String(o));
43
- }), this.get(
44
- `/dashboard-builder/embed/${e}/render?${r}`,
45
- { "x-embed-token": t }
46
- );
47
- }
48
- // ── Single Report Render ────────────────────────────────────────────────────
49
- async getReportRender(e, t, s, r = {}) {
50
- const i = new URLSearchParams({
51
- embedType: "report",
52
- embedLevel: "report",
53
- componentId: t
54
- });
55
- return r.filters && Object.entries(r.filters).forEach(([a, o]) => {
56
- o != null && o !== "" && i.set(`filter[${a}]`, Array.isArray(o) ? o.join(",") : String(o));
57
- }), this.get(
58
- `/dashboard-builder/embed/${e}/render?${i}`,
59
- { "x-embed-token": s }
60
- );
61
- }
62
- // ── API Key Management ──────────────────────────────────────────────────────
63
- async createApiKey(e, t) {
64
- return this.post("/dashboard-builder/api-keys", e, {
65
- Authorization: `Bearer ${t}`
66
- });
67
- }
68
- async listApiKeys(e) {
69
- return this.get("/dashboard-builder/api-keys", {
70
- Authorization: `Bearer ${e}`
71
- });
72
- }
73
- async deleteApiKey(e, t) {
74
- await this.delete(`/dashboard-builder/api-keys/${e}`, {
75
- Authorization: `Bearer ${t}`
76
- });
77
- }
78
- // ── Paginated Table Data ────────────────────────────────────────────────────
79
- /**
80
- * Fetches one page of data for an embedded data-table component.
81
- * Called by TableRenderer whenever the user navigates to a different page.
82
- * Mirrors: GET /dashboard-builder/embed/:dashboardId/table/:componentId/data
83
- */
84
- async getTablePage(e, t, s, r, i) {
85
- return this.get(
86
- `/dashboard-builder/embed/${e}/table/${t}/data?page=${s}&pageSize=${r}`,
87
- { "x-embed-token": i }
88
- );
89
- }
90
- // ── Filter Options ──────────────────────────────────────────────────────────
91
- /**
92
- * Fetches distinct option values for a dropdown / multi-select filter.
93
- * Mirrors the main dashboard call:
94
- * GET /dashboard-builder/{dashboardId}/filters/{filterId}/options
95
- * The embed token is sent so the backend can validate the caller has
96
- * access to this dashboard without requiring an admin JWT.
97
- */
98
- async getFilterOptions(e, t, s) {
99
- try {
100
- const r = await this.get(
101
- `/dashboard-builder/embed/${e}/filters/${t}/options`,
102
- { "x-embed-token": s }
103
- );
104
- return Array.isArray(r) ? r : Array.isArray(r == null ? void 0 : r.options) ? r.options : Array.isArray(r == null ? void 0 : r.data) ? r.data : [];
105
- } catch (r) {
106
- return [];
107
- }
108
- }
109
- clearTokenCache() {
110
- this.tokenCache.clear();
111
- }
112
- // ── Private helpers ─────────────────────────────────────────────────────────
113
- async get(e, t = {}) {
114
- const s = await fetch(`${this.baseUrl}${e}`, {
115
- method: "GET",
116
- headers: { "Content-Type": "application/json", ...t }
117
- });
118
- return this.handleResponse(s);
119
- }
120
- async post(e, t, s = {}) {
121
- const r = await fetch(`${this.baseUrl}${e}`, {
122
- method: "POST",
123
- headers: { "Content-Type": "application/json", ...s },
124
- body: JSON.stringify(t)
125
- });
126
- return this.handleResponse(r);
127
- }
128
- async delete(e, t = {}) {
129
- const s = await fetch(`${this.baseUrl}${e}`, {
130
- method: "DELETE",
131
- headers: { "Content-Type": "application/json", ...t }
132
- });
133
- if (!s.ok) throw new Error(`Deepspot SDK: DELETE ${e} failed (${s.status})`);
134
- }
135
- async handleResponse(e) {
136
- if (!e.ok) {
137
- let s = `HTTP ${e.status}`;
138
- try {
139
- const r = await e.json();
140
- s = (r == null ? void 0 : r.message) || (r == null ? void 0 : r.error) || s;
141
- } catch (r) {
142
- }
143
- throw new Error(`Deepspot SDK: ${s}`);
144
- }
145
- const t = await e.json();
146
- return (t == null ? void 0 : t.data) !== void 0 ? t.data : t;
147
- }
148
- }
149
- class Z {
150
- constructor(e) {
151
- var t, s;
152
- this.destroyed = !1, this.refreshTimer = null, this.opts = e, this.filters = { ...e.activeFilters }, this.activePageId = (t = e.activePageId) != null ? t : "", this.activeTabId = (s = e.activeTabId) != null ? s : "";
153
- }
154
- /** Returns a copy of the currently applied filters */
155
- getActiveFilters() {
156
- return { ...this.filters };
157
- }
158
- /** Apply a single filter and re-fetch data */
159
- setFilter(e, t) {
160
- this.filters[e] = t, this.scheduleRefresh();
161
- }
162
- /** Apply multiple filters at once and re-fetch */
163
- setFilters(e) {
164
- Object.assign(this.filters, e), this.scheduleRefresh();
165
- }
166
- /** v2 alias for setFilter — sets a global filter and re-fetches data */
167
- setGlobalFilter(e, t) {
168
- this.setFilter(e, t);
169
- }
170
- /** v2: clear all active filters and re-fetch data */
171
- clearFilters() {
172
- this.filters = {}, this.scheduleRefresh();
173
- }
174
- /** Navigate to a different page (dashboard embed only) */
175
- goToPage(e) {
176
- this.opts.embedType === "dashboard" && (this.activePageId = e, this.opts.renderer.goToPage(e));
177
- }
178
- /** Navigate to a specific tab on a specific page */
179
- goToTab(e, t) {
180
- this.opts.embedType === "dashboard" && (this.activePageId = e, this.activeTabId = t, this.opts.renderer.goToTab(e, t));
181
- }
182
- /** Force re-fetch all data from backend */
183
- async refresh() {
184
- if (!this.destroyed)
185
- try {
186
- const e = await this.fetchData();
187
- this.opts.renderer.update(e);
188
- } catch (e) {
189
- console.error("Deepspot SDK: refresh failed", e);
190
- }
191
- }
192
- /** Export PDF (dashboard embed only) */
193
- exportPDF() {
194
- this.opts.embedType === "dashboard" && this.opts.renderer.exportPDF();
195
- }
196
- /** Remove the embed and clean up */
197
- destroy() {
198
- this.destroyed = !0, this.refreshTimer && clearTimeout(this.refreshTimer), this.opts.renderer.destroy(), this.opts.apiClient.clearTokenCache();
199
- }
200
- // ── Private ─────────────────────────────────────────────────────────────────
201
- /** Debounce rapid filter changes into a single fetch */
202
- scheduleRefresh() {
203
- this.refreshTimer && clearTimeout(this.refreshTimer), this.refreshTimer = setTimeout(() => this.refresh(), 300);
204
- }
205
- async fetchData() {
206
- const { apiClient: e, dashboardId: t, componentId: s, token: r, embedType: i, embedLevel: a } = this.opts;
207
- return i === "report" && s ? e.getReportRender(t, s, r, {
208
- filters: this.filters
209
- }) : e.getDashboardRender(t, r, {
210
- embedLevel: a,
211
- pageId: this.activePageId || void 0,
212
- tabId: this.activeTabId || void 0,
213
- filters: this.filters
214
- });
215
- }
216
- }
217
- const oe = 24, A = 10, D = 12;
218
- class Q {
219
- /**
220
- * @param containerWidth Actual pixel width of the grid container.
221
- * @param minY Minimum y value across all components (normalises
222
- * the grid so the topmost component starts at top=6px).
223
- */
224
- constructor(e, t = 0) {
225
- this.containerWidth = e, this.minY = t;
226
- }
227
- /** Pixel width of one column unit */
228
- get colWidth() {
229
- return this.containerWidth / oe;
230
- }
231
- /** Convert grid position {x,y,w,h} → CSS absolute pixel values */
232
- toPx(e) {
233
- const t = this.colWidth, s = D / 2;
234
- return {
235
- left: e.x * t + s,
236
- top: (e.y - this.minY) * A + s,
237
- width: e.w * t - D,
238
- height: e.h * A - D
239
- };
240
- }
241
- /** Total pixel height required to fit all positioned components */
242
- static totalHeight(e) {
243
- if (!e.length) return 400;
244
- const t = Math.min(...e.map((r) => r.y));
245
- return (Math.max(...e.map((r) => r.y + r.h)) - t) * A + 40;
246
- }
247
- /** Apply absolute positioning styles directly to a DOM element */
248
- applyStyles(e, t) {
249
- const s = this.toPx(t);
250
- e.style.position = "absolute", e.style.left = `${s.left}px`, e.style.top = `${s.top}px`, e.style.width = `${s.width}px`, e.style.height = `${s.height}px`;
251
- }
252
- }
253
- let z = null;
254
- function de() {
255
- return z || (z = import("./apexcharts.common-k3hLWpB8.js").then((k) => k.a)), z;
256
- }
257
- class se {
258
- constructor(e) {
259
- this.chart = null, this.container = e;
260
- }
261
- async render(e, t, s, r) {
262
- if (!t || t.length === 0) {
263
- this.renderEmpty(e.title);
264
- return;
265
- }
266
- const i = this.buildOptions(e, t, s, r);
267
- if (!i) {
268
- this.renderEmpty(e.title);
269
- return;
270
- }
271
- this.container.innerHTML = `
272
- <div class="ds-chart-card">
273
- <div class="ds-chart-title">${e.title || ""}</div>
274
- <div class="ds-chart-body" id="ds-chart-body-${e.id}"></div>
275
- </div>
276
- `;
277
- const a = this.container.querySelector(`#ds-chart-body-${e.id}`);
278
- if (a)
279
- try {
280
- this.chart && this.chart.destroy();
281
- const { default: o } = await de();
282
- this.chart = new o(a, i), this.chart.render();
283
- } catch (o) {
284
- console.error("[Deepspot SDK] ChartRenderer error:", o), a.innerHTML = '<div class="ds-chart-empty">Chart render error</div>';
285
- }
286
- }
287
- async update(e, t, s) {
288
- if (!this.chart || !t || t.length === 0) {
289
- await this.render(e, t, s);
290
- return;
291
- }
292
- const { series: r, categories: i } = this.extractSeriesAndCategories(e, t);
293
- try {
294
- this.chart.updateOptions({
295
- series: r,
296
- xaxis: { categories: i },
297
- theme: { mode: s }
298
- });
299
- } catch (a) {
300
- this.render(e, t, s);
301
- }
302
- }
303
- destroy() {
304
- var e;
305
- try {
306
- (e = this.chart) == null || e.destroy();
307
- } catch (t) {
308
- }
309
- this.chart = null;
310
- }
311
- // ── Private ─────────────────────────────────────────────────────────────────
312
- buildOptions(e, t, s, r) {
313
- const i = e.type;
314
- return i === "pie" || i === "donut" ? this.buildPieOptions(e, t, s, r) : i === "bar" || i === "line" || i === "area" ? this.buildCartesianOptions(e, t, s, r) : null;
315
- }
316
- buildCartesianOptions(e, t, s, r) {
317
- var h;
318
- const { series: i, categories: a, xAxisLabel: o, yAxisLabel: c, seriesColors: d } = this.extractSeriesAndCategories(e, t), f = e.properties || {}, g = ["#4f46e5", "#10b981", "#f59e0b", "#ef4444", "#3b82f6", "#8b5cf6", "#ec4899", "#14b8a6"];
319
- let u;
320
- if (d.length > 0 && d.length === i.length)
321
- u = d;
322
- else {
323
- const b = f.colors ? Object.values(f.colors).filter(Boolean) : [];
324
- u = b.length > 0 ? b : g;
325
- }
326
- const n = s === "dark";
327
- return {
328
- chart: {
329
- type: e.type === "area" ? "area" : e.type === "line" ? "line" : "bar",
330
- toolbar: { show: !1 },
331
- background: "transparent",
332
- animations: { enabled: !0, speed: 400 },
333
- fontFamily: "inherit",
334
- // Explicit height prevents ApexCharts from guessing the flex-child height
335
- // at render time (which can read as 0 before the browser settles layout).
336
- height: r != null ? r : "100%"
337
- },
338
- theme: { mode: s },
339
- series: i,
340
- xaxis: {
341
- categories: a,
342
- title: { text: o },
343
- labels: {
344
- rotate: a.length > 8 ? -45 : 0,
345
- style: { colors: n ? "#94a3b8" : "#6b7280", fontSize: "11px" }
346
- },
347
- axisBorder: { color: n ? "#334155" : "#e5e7eb" },
348
- axisTicks: { color: n ? "#334155" : "#e5e7eb" }
349
- },
350
- yaxis: {
351
- title: { text: c },
352
- labels: { style: { colors: n ? "#94a3b8" : "#6b7280", fontSize: "11px" } }
353
- },
354
- colors: u,
355
- legend: {
356
- show: i.length > 1 || ((h = f.showLegend) != null ? h : !1),
357
- position: "bottom",
358
- labels: { colors: n ? "#94a3b8" : "#6b7280" }
359
- },
360
- grid: {
361
- borderColor: n ? "#1e293b" : "#f3f4f6",
362
- strokeDashArray: 4,
363
- padding: {
364
- bottom: a.length > 8 ? 60 : 10
365
- }
366
- },
367
- tooltip: { theme: s },
368
- stroke: {
369
- curve: "smooth",
370
- width: e.type === "bar" ? 0 : 2
371
- },
372
- fill: e.type === "area" ? { type: "gradient", gradient: { shadeIntensity: 0.5, opacityFrom: 0.4, opacityTo: 0 } } : {},
373
- dataLabels: { enabled: !1 },
374
- plotOptions: {
375
- bar: {
376
- borderRadius: 4,
377
- distributed: i.length === 1
378
- }
379
- }
380
- };
381
- }
382
- buildPieOptions(e, t, s, r) {
383
- const i = e.properties || {}, a = t.length > 0 ? Object.keys(t[0]) : [], o = a[0] || "label", c = a[1] || "value", d = t.map((b) => {
384
- var l;
385
- return String((l = b[o]) != null ? l : "");
386
- }), f = t.map((b) => Number(b[c]) || 0), g = s === "dark", u = ["#4f46e5", "#10b981", "#f59e0b", "#ef4444", "#3b82f6", "#8b5cf6", "#ec4899", "#14b8a6"], n = i.colors ? Object.values(i.colors).filter(Boolean) : [], h = n.length > 0 ? n : u;
387
- return {
388
- chart: {
389
- type: e.type,
390
- toolbar: { show: !1 },
391
- background: "transparent",
392
- fontFamily: "inherit",
393
- height: r != null ? r : "100%"
394
- },
395
- theme: { mode: s },
396
- series: f,
397
- labels: d,
398
- colors: h,
399
- legend: {
400
- position: "bottom",
401
- labels: { colors: g ? "#94a3b8" : "#6b7280" }
402
- },
403
- tooltip: { theme: s },
404
- dataLabels: { enabled: d.length <= 8 },
405
- plotOptions: {
406
- pie: {
407
- donut: { size: e.type === "donut" ? "65%" : "0%" }
408
- }
409
- }
410
- };
411
- }
412
- extractSeriesAndCategories(e, t) {
413
- const s = e.properties || {}, r = t.length > 0 ? Object.keys(t[0]) : [], i = s.xAxis || r[0] || "x";
414
- let a = [];
415
- if (s.selectedYAxisColumn)
416
- try {
417
- const n = typeof s.selectedYAxisColumn == "string" ? JSON.parse(s.selectedYAxisColumn) : s.selectedYAxisColumn;
418
- Array.isArray(n) && (a = n.map(
419
- (h) => typeof h == "string" ? { column: h } : h
420
- ));
421
- } catch (n) {
422
- }
423
- if (!a.length && s.yAxis) {
424
- const n = s.yAxis;
425
- a = (Array.isArray(n) ? n : [n]).map((b) => ({ column: String(b) }));
426
- }
427
- !a.length && r.length > 1 && (a = r.slice(1).map((n) => ({ column: n })));
428
- const o = t.map((n) => {
429
- var h;
430
- return String((h = n[i]) != null ? h : "");
431
- }), c = t.length > 0 ? Object.keys(t[0]) : [], d = a.map((n) => {
432
- const h = c.includes(n.column) ? n.column : null, b = h ? null : c.find((p) => p.includes(n.column) || n.column.includes(p)), l = !h && !b ? c.find((p) => {
433
- var y;
434
- return p !== i && !isNaN(Number((y = t[0]) == null ? void 0 : y[p]));
435
- }) : null, m = h || b || l || n.column;
436
- return {
437
- // Use the column name directly as the series label (matches builder behaviour).
438
- // col.label often stores the chart title, not the series name — ignore it.
439
- name: n.column,
440
- data: t.map((p) => {
441
- const y = p[m];
442
- return y != null ? Number(y) : null;
443
- })
444
- };
445
- }), f = a.map((n) => n.color || n.colorHex || "").filter(Boolean), g = s.xAxisLabel || i, u = s.yAxisLabel || (a.length === 1 ? a[0].column : "");
446
- return { series: d, categories: o, xAxisLabel: g, yAxisLabel: u, seriesColors: f };
447
- }
448
- renderEmpty(e) {
449
- this.container.innerHTML = `
450
- <div class="ds-chart-card">
451
- ${e ? `<div class="ds-chart-title">${e}</div>` : ""}
452
- <div class="ds-chart-empty">No data available</div>
453
- </div>
454
- `;
455
- }
456
- }
457
- class re {
458
- constructor() {
459
- this.columns = [], this.allData = [], this.currentRows = [], this.totalRows = 0, this.currentPage = 1, this.currentPageSize = 10, this.searchTerm = "", this.isLoading = !1;
460
- }
461
- // ── Public API ─────────────────────────────────────────────────────────────
462
- render(e, t, s, r) {
463
- var f;
464
- this.container = e, this.component = t, this.fetchPage = r, this.searchTerm = "", this.currentPage = 1, this.currentPageSize = 10;
465
- const i = s && !Array.isArray(s) && "rows" in s;
466
- if (i) {
467
- const g = s;
468
- this.currentRows = g.rows, this.allData = [], this.totalRows = g.total, this.currentPage = g.page, this.currentPageSize = g.pageSize;
469
- } else
470
- this.allData = s || [], this.currentRows = [], this.totalRows = this.allData.length, this.currentPage = 1;
471
- if (i && s.rows.length === 0 && s.total === 0 || !i && this.allData.length === 0) {
472
- e.innerHTML = `
473
- <div class="ds-table-card">
474
- <div class="ds-table-header-row">
475
- <span class="ds-table-dot"></span>
476
- <div class="ds-table-title">${this.escape(t.title || "")}</div>
477
- </div>
478
- <div class="ds-chart-empty">No data available</div>
479
- </div>
480
- `;
481
- return;
482
- }
483
- const a = i ? s.rows[0] : this.allData[0], o = a ? Object.keys(a) : [], c = ((f = t.properties) == null ? void 0 : f.columns) || [], d = c.length ? c.filter((g) => o.includes(g)) : [];
484
- this.columns = d.length ? d : o, this.paint();
485
- }
486
- // ── Rendering ──────────────────────────────────────────────────────────────
487
- paint() {
488
- const { rows: e, startNum: t } = this.getPageRows(), s = Math.max(1, Math.ceil(this.totalRows / this.currentPageSize)), r = (this.currentPage - 1) * this.currentPageSize + 1, i = this.fetchPage ? Math.min(r - 1 + (this.isLoading ? this.currentPageSize : e.length), this.totalRows) : Math.min(this.currentPage * this.currentPageSize, this.totalRows), a = this.columns.map((u) => `<th>${this.escape(this.formatHeader(u))}</th>`).join(""), o = e.length === 0 ? `<tr><td colspan="${this.columns.length + 1}" style="text-align:center;padding:20px;color:#9ca3af">No results found</td></tr>` : e.map((u, n) => {
489
- const h = this.columns.map((b) => {
490
- const l = u[b];
491
- return `<td title="${this.escape(String(l != null ? l : ""))}">${this.escape(this.formatValue(l))}</td>`;
492
- }).join("");
493
- return `<tr><td class="ds-table-sno">${t + n}</td>${h}</tr>`;
494
- }).join(""), c = this.currentPageSize, d = [10, 25, 50, 100].map((u) => `<option value="${u}"${u === c ? " selected" : ""}>${u}</option>`).join(""), f = this.currentPage <= 1, g = this.currentPage >= s;
495
- this.container.innerHTML = `
496
- <div class="ds-table-card">
497
- <div class="ds-table-header-row">
498
- <div class="ds-table-title-wrap">
499
- <span class="ds-table-dot"></span>
500
- <span class="ds-table-title">${this.escape(this.component.title || "")}</span>
501
- </div>
502
- </div>
503
- <div class="ds-table-scroll">
504
- <table class="ds-table">
505
- <thead><tr><th class="ds-table-sno-th">S.No</th>${a}</tr></thead>
506
- <tbody>${o}</tbody>
507
- </table>
508
- </div>
509
- <div class="ds-table-footer">
510
- <div class="ds-table-info">
511
- <span>${r.toLocaleString()}–${i.toLocaleString()} of ${this.totalRows.toLocaleString()}</span>
512
- <span class="ds-table-rows-label">Rows:</span>
513
- <select class="ds-table-page-size">${d}</select>
514
- </div>
515
- <div class="ds-table-pagination">
516
- <button class="ds-table-pg-btn" data-action="first" ${f ? "disabled" : ""}>&#171;</button>
517
- <button class="ds-table-pg-btn" data-action="prev" ${f ? "disabled" : ""}>Previous</button>
518
- <span class="ds-table-pg-info">Page ${this.currentPage} of ${s}</span>
519
- <button class="ds-table-pg-btn" data-action="next" ${g ? "disabled" : ""}>Next</button>
520
- <button class="ds-table-pg-btn" data-action="last" ${g ? "disabled" : ""}>&#187;</button>
521
- </div>
522
- </div>
523
- ${this.isLoading ? '<div class="ds-table-loading-overlay"><div class="ds-embed-spinner"></div></div>' : ""}
524
- </div>
525
- `, this.attachEvents();
526
- }
527
- attachEvents() {
528
- const e = this.container.querySelector(".ds-table-search");
529
- e == null || e.addEventListener("input", () => {
530
- this.searchTerm = e.value, this.currentPage = 1, this.fetchPage || (this.totalRows = this.filteredRows().length, this.paint());
531
- });
532
- const t = this.container.querySelector(".ds-table-page-size");
533
- t == null || t.addEventListener("change", () => {
534
- this.currentPageSize = parseInt(t.value, 10), this.currentPage = 1, this.fetchPage ? this.loadPage(1) : (this.totalRows = this.filteredRows().length, this.paint());
535
- }), this.container.querySelectorAll("[data-action]").forEach((s) => {
536
- s.addEventListener("click", () => {
537
- if (s.disabled) return;
538
- const r = Math.max(1, Math.ceil(this.totalRows / this.currentPageSize)), i = s.dataset.action;
539
- let a = this.currentPage;
540
- i === "first" ? a = 1 : i === "prev" ? a = Math.max(1, this.currentPage - 1) : i === "next" ? a = Math.min(r, this.currentPage + 1) : i === "last" && (a = r), (a !== this.currentPage || i === "first" || i === "last") && (this.currentPage = a, this.fetchPage ? this.loadPage(a) : this.paint());
541
- });
542
- });
543
- }
544
- // ── Server-side page load ──────────────────────────────────────────────────
545
- async loadPage(e) {
546
- if (this.fetchPage) {
547
- this.isLoading = !0, this.currentPage = e, this.paint();
548
- try {
549
- const t = await this.fetchPage(e, this.currentPageSize);
550
- this.currentRows = t.rows || [], this.totalRows = Number(t.total) || this.currentRows.length, this.currentPage = Number(t.page) || e;
551
- } catch (t) {
552
- console.error("[Deepspot SDK] TableRenderer: page fetch failed", t);
553
- } finally {
554
- this.isLoading = !1, this.paint();
555
- }
556
- }
557
- }
558
- // ── Data helpers ───────────────────────────────────────────────────────────
559
- getPageRows() {
560
- const e = (this.currentPage - 1) * this.currentPageSize + 1;
561
- if (this.fetchPage)
562
- return { rows: this.currentRows, startNum: e };
563
- const t = this.filteredRows(), s = (this.currentPage - 1) * this.currentPageSize;
564
- return { rows: t.slice(s, s + this.currentPageSize), startNum: e };
565
- }
566
- filteredRows() {
567
- if (!this.searchTerm) return this.allData;
568
- const e = this.searchTerm.toLowerCase();
569
- return this.allData.filter(
570
- (t) => this.columns.some((s) => {
571
- var r;
572
- return String((r = t[s]) != null ? r : "").toLowerCase().includes(e);
573
- })
574
- );
575
- }
576
- // ── Formatters ─────────────────────────────────────────────────────────────
577
- formatHeader(e) {
578
- return e.replace(/_/g, " ").replace(/\b\w/g, (t) => t.toUpperCase());
579
- }
580
- formatValue(e) {
581
- return e == null ? "—" : typeof e == "number" ? e % 1 === 0 ? e.toLocaleString() : e.toFixed(2) : String(e);
582
- }
583
- escape(e) {
584
- return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
585
- }
586
- }
587
- class ie {
588
- render(e, t, s) {
589
- var h;
590
- const r = t.properties || {};
591
- let i = null;
592
- if (s && s.length > 0) {
593
- const b = s[0], l = r.metric || Object.keys(b)[0], m = b[l];
594
- if (m !== void 0)
595
- i = m;
596
- else {
597
- const p = Object.keys(b).find(
598
- (y) => y.includes(l) || l.includes(y)
599
- );
600
- i = b[p != null ? p : Object.keys(b)[0]];
601
- }
602
- }
603
- let a;
604
- if (i == null)
605
- try {
606
- const b = (h = r == null ? void 0 : r.data) == null ? void 0 : h.dataContent, l = b ? typeof b == "string" ? JSON.parse(b) : b : null;
607
- a = l != null && l.value ? String(l.value) : "—";
608
- } catch (b) {
609
- a = "—";
610
- }
611
- else
612
- a = this.formatValue(i, r);
613
- const o = r.styleConfig || {}, c = o.backgroundColor || r.backgroundColor || "", d = o.color || o.textColor || r.textColor || "", f = t.title || "", g = c ? `background:${c};border:none;` : "", u = d ? `color:${d};` : "", n = d ? `color:${d};opacity:0.7;` : "";
614
- e.innerHTML = `
615
- <div class="ds-card"${g ? ` style="${g}"` : ""}>
616
- <div class="ds-card-label"${n ? ` style="${n}"` : ""}>${f}</div>
617
- <div class="ds-card-value"${u ? ` style="${u}"` : ""}>${a}</div>
618
- </div>
619
- `;
620
- }
621
- formatValue(e, t) {
622
- if (e == null) return "—";
623
- const s = Number(e);
624
- if (isNaN(s)) return String(e);
625
- const r = (t == null ? void 0 : t.prefix) || (t == null ? void 0 : t.currencySymbol) || "", i = (t == null ? void 0 : t.suffix) || "", a = s % 1 === 0 ? s.toLocaleString() : parseFloat(s.toFixed(2)).toLocaleString();
626
- return `${r}${a}${i}`;
627
- }
628
- }
629
- class ne {
630
- constructor(e, t) {
631
- this.currentValues = {}, this.container = e, this.onFilterChange = t;
632
- }
633
- render(e, t = {}) {
634
- if (!e || e.length === 0) {
635
- this.container.style.display = "none";
636
- return;
637
- }
638
- this.container.style.display = "", this.currentValues = { ...t }, this.container.innerHTML = `<div class="ds-filter-bar">${e.map((s) => this.renderFilter(s, t[s.applyToField || s.filterId])).join("")}</div>`, this.attachListeners(e);
639
- }
640
- /**
641
- * Populate a dropdown / multi-select with options fetched from the API.
642
- * Called asynchronously after the filter bar is rendered.
643
- */
644
- updateOptions(e, t) {
645
- const s = this.container.querySelector(
646
- `select[data-filter-id="${e}"]`
647
- );
648
- if (!s) return;
649
- const r = s.value;
650
- s.innerHTML = '<option value="">All</option>' + t.map(
651
- (i) => `<option value="${this.escAttr(i)}">${this.escText(i)}</option>`
652
- ).join(""), r && t.includes(r) && (s.value = r);
653
- }
654
- /** Update the stored values without re-rendering (for programmatic setFilter) */
655
- updateValues(e) {
656
- Object.assign(this.currentValues, e), Object.entries(e).forEach(([t, s]) => {
657
- const r = this.container.querySelector(`[data-filter-id="${t}"]`);
658
- r && s !== void 0 && (r.value = String(s));
659
- });
660
- }
661
- // ── Private ─────────────────────────────────────────────────────────────────
662
- renderFilter(e, t) {
663
- const s = `ds-filter-${e.filterId}`, r = `<label class="ds-filter-label" for="${s}">${e.label}</label>`;
664
- switch (e.type) {
665
- case "dropdown":
666
- return `
667
- <div class="ds-filter-item">
668
- ${r}
669
- <select class="ds-filter-select" id="${s}" data-filter-id="${e.filterId}">
670
- <option value="">All</option>
671
- ${(e.options || []).map((o) => `
672
- <option value="${this.escAttr(o)}" ${t === o ? "selected" : ""}>
673
- ${this.escText(o)}
674
- </option>
675
- `).join("")}
676
- </select>
677
- </div>`;
678
- case "multi-select":
679
- return `
680
- <div class="ds-filter-item">
681
- ${r}
682
- <select class="ds-filter-select" id="${s}" data-filter-id="${e.filterId}" multiple size="1">
683
- ${(e.options || []).map((o) => `
684
- <option value="${this.escAttr(o)}">${this.escText(o)}</option>
685
- `).join("")}
686
- </select>
687
- </div>`;
688
- case "date-range":
689
- const i = (t == null ? void 0 : t.from) || "", a = (t == null ? void 0 : t.to) || "";
690
- return `
691
- <div class="ds-filter-item">
692
- ${r}
693
- <div class="ds-date-range-inputs">
694
- <input type="date" class="ds-filter-input"
695
- data-filter-id="${e.filterId}" data-date-part="from"
696
- value="${i}" />
697
- <span>–</span>
698
- <input type="date" class="ds-filter-input"
699
- data-filter-id="${e.filterId}" data-date-part="to"
700
- value="${a}" />
701
- </div>
702
- </div>`;
703
- case "text-input":
704
- return `
705
- <div class="ds-filter-item">
706
- ${r}
707
- <input type="text" class="ds-filter-input" id="${s}"
708
- data-filter-id="${e.filterId}"
709
- value="${this.escAttr(String(t != null ? t : ""))}"
710
- placeholder="Search..." />
711
- </div>`;
712
- case "number-input":
713
- return `
714
- <div class="ds-filter-item">
715
- ${r}
716
- <input type="number" class="ds-filter-input" id="${s}"
717
- data-filter-id="${e.filterId}"
718
- value="${this.escAttr(String(t != null ? t : ""))}" />
719
- </div>`;
720
- default:
721
- return "";
722
- }
723
- }
724
- attachListeners(e) {
725
- const t = {};
726
- this.container.querySelectorAll("[data-filter-id]").forEach((s) => {
727
- const r = s.dataset.filterId, i = s.dataset.datePart, a = e.find((c) => c.filterId === r), o = () => {
728
- let c;
729
- if ((a == null ? void 0 : a.type) === "date-range" && i)
730
- t[r] = t[r] || {}, t[r][i] = s.value, c = { ...t[r] };
731
- else if ((a == null ? void 0 : a.type) === "multi-select") {
732
- const d = s;
733
- c = Array.from(d.selectedOptions).map((f) => f.value).filter(Boolean);
734
- } else
735
- c = s.value;
736
- this.currentValues[r] = c, this.onFilterChange(r, c);
737
- };
738
- s.addEventListener(
739
- (a == null ? void 0 : a.type) === "text-input" ? "input" : "change",
740
- o
741
- );
742
- });
743
- }
744
- escAttr(e) {
745
- return String(e != null ? e : "").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
746
- }
747
- escText(e) {
748
- return String(e != null ? e : "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
749
- }
750
- }
751
- const E = class E {
752
- constructor(e, t) {
753
- this.activePageId = "", this.activeTabId = "", this.activeFilters = {}, this.chartRenderers = /* @__PURE__ */ new Map(), this.tableRenderer = new re(), this.cardRenderer = new ie(), this.isLoadingTab = !1, this.tooltipEl = null, this.root = e, this.opts = t, this.activeFilters = { ...t.initialFilters };
754
- }
755
- // ── Initial full render ───────────────────────────────────────────────────
756
- render(e) {
757
- var t, s;
758
- this.renderData = e, this.activePageId = e.activePage, this.activeTabId = e.activeTab, this.buildShell(), this.renderNavigation(), this.renderFilterBar(), this.renderGrid(), (s = (t = this.opts).onReady) == null || s.call(t);
759
- }
760
- // ── Called after lazy tab fetch completes ─────────────────────────────────
761
- update(e) {
762
- this.renderData = e, this.activePageId = e.activePage, this.activeTabId = e.activeTab, this.isLoadingTab = !1, this.syncNavHighlight(), this.updateFilterBar(), this.renderGrid();
763
- }
764
- goToPage(e) {
765
- const t = this.getTabsForPage(e)[0];
766
- t && this.triggerTabSwitch(e, t.id);
767
- }
768
- goToTab(e, t) {
769
- this.triggerTabSwitch(e, t);
770
- }
771
- destroy() {
772
- this.chartRenderers.forEach((e) => e.destroy()), this.chartRenderers.clear(), this.root.innerHTML = "";
773
- }
774
- exportPDF() {
775
- var u, n;
776
- const e = this.root.querySelector("#ds-grid");
777
- if (!e) return;
778
- const t = this.root.style.height, s = this.root.style.overflow;
779
- this.root.style.height = "auto", this.root.style.overflow = "visible";
780
- const r = this.root.querySelector("#ds-canvas"), i = (u = r == null ? void 0 : r.style.height) != null ? u : "", a = (n = r == null ? void 0 : r.style.overflow) != null ? n : "";
781
- r && (r.style.height = "auto", r.style.overflow = "visible");
782
- const o = Array.from(e.querySelectorAll(".ds-table-scroll")), c = o.map((h) => ({
783
- el: h,
784
- overflow: h.style.overflow,
785
- maxHeight: h.style.maxHeight,
786
- height: h.style.height
787
- }));
788
- o.forEach((h) => {
789
- h.style.overflow = "visible", h.style.maxHeight = "none", h.style.height = "auto";
790
- });
791
- const d = e.offsetWidth, f = parseInt(e.style.height, 10) || e.offsetHeight, g = e.getBoundingClientRect().top + window.scrollY;
792
- import("./html2canvas.esm-CzwMv54K.js").then(
793
- ({ default: h }) => h(e, {
794
- scale: 2,
795
- useCORS: !0,
796
- allowTaint: !0,
797
- logging: !1,
798
- width: d,
799
- height: f,
800
- windowWidth: d,
801
- windowHeight: f,
802
- scrollX: 0,
803
- scrollY: -g
804
- }).then((b) => {
805
- this.root.style.height = t, this.root.style.overflow = s, r && (r.style.height = i, r.style.overflow = a), c.forEach(({ el: l, overflow: m, maxHeight: p, height: y }) => {
806
- l.style.overflow = m, l.style.maxHeight = p, l.style.height = y;
807
- }), import("./jspdf.es.min-Ge0fRUwj.js").then((l) => l.j).then(({ jsPDF: l }) => {
808
- const m = b.width / 2, p = b.height / 2, y = new l({
809
- orientation: m >= p ? "landscape" : "portrait",
810
- unit: "px",
811
- format: [m, p]
812
- });
813
- y.addImage(b.toDataURL("image/png"), "PNG", 0, 0, m, p), y.save(`${this.renderData.dashboard.name || "dashboard"}.pdf`);
814
- });
815
- })
816
- );
817
- }
818
- exportCSV() {
819
- this.renderData.components.forEach((e) => {
820
- const t = this.renderData.data[e.id];
821
- if (!(t != null && t.length) || !["table", "bar", "line", "area"].includes(e.type)) return;
822
- const s = Object.keys(t[0]), r = [s.join(","), ...t.map(
823
- (a) => s.map((o) => {
824
- var c;
825
- return JSON.stringify((c = a[o]) != null ? c : "");
826
- }).join(",")
827
- )].join(`
828
- `);
829
- Object.assign(document.createElement("a"), {
830
- href: URL.createObjectURL(new Blob([r], { type: "text/csv" })),
831
- download: `${e.title || e.id}.csv`
832
- }).click();
833
- });
834
- }
835
- updateFilterValues(e) {
836
- var t;
837
- Object.assign(this.activeFilters, e), (t = this.filterRenderer) == null || t.updateValues(e);
838
- }
839
- // ── Shell ─────────────────────────────────────────────────────────────────
840
- buildShell() {
841
- const e = this.opts.embedLevel, t = this.renderData.dashboard.pages, s = e === "dashboard" && t.length > 1, r = e === "page" || e === "dashboard";
842
- this.root.innerHTML = `
843
- ${this.opts.hideExport ? "" : '<div class="ds-toolbar" id="ds-toolbar"></div>'}
844
- ${s ? '<nav class="ds-page-nav" id="ds-page-nav"></nav>' : ""}
845
- ${r ? '<nav class="ds-page-nav" id="ds-tab-nav"></nav>' : ""}
846
- <div id="ds-filter-container"></div>
847
- <div class="ds-canvas" id="ds-canvas">
848
- <div class="ds-grid" id="ds-grid"></div>
849
- </div>
850
- <div id="ds-tab-loading" style="display:none;position:absolute;inset:0;
851
- background:rgba(255,255,255,0.5);align-items:center;justify-content:center;">
852
- <div class="ds-embed-spinner"></div>
853
- </div>
854
- `, this.root.style.position = "relative", this.opts.hideExport || this.buildExportToolbar();
855
- const i = this.root.querySelector("#ds-filter-container");
856
- this.filterRenderer = new ne(i, (a, o) => {
857
- var g, u, n, h, b;
858
- const c = (g = this.renderData.filters) == null ? void 0 : g.find((l) => l.filterId === a), d = (u = this.renderData.components) == null ? void 0 : u.find(
859
- (l) => l.type === "inline-filter" && l.id === a
860
- ), f = (c == null ? void 0 : c.applyToField) || ((n = d == null ? void 0 : d.properties) == null ? void 0 : n.applyToField) || a;
861
- this.activeFilters[f] = o, (b = (h = this.opts).onFilterChange) == null || b.call(h, this.activeFilters), this.opts.onTabSwitch(this.activePageId, this.activeTabId);
862
- });
863
- }
864
- buildExportToolbar() {
865
- var t, s;
866
- const e = this.root.querySelector("#ds-toolbar");
867
- e && (e.innerHTML = `
868
- <button class="ds-toolbar-btn" id="ds-btn-pdf">⬇ PDF</button>
869
- <button class="ds-toolbar-btn" id="ds-btn-csv" style="display:none">⬇ CSV</button>
870
- `, (t = e.querySelector("#ds-btn-pdf")) == null || t.addEventListener("click", () => this.exportPDF()), (s = e.querySelector("#ds-btn-csv")) == null || s.addEventListener("click", () => this.exportCSV()));
871
- }
872
- // ── Navigation ────────────────────────────────────────────────────────────
873
- renderNavigation() {
874
- this.renderPageNav(), this.renderTabNav(this.activePageId);
875
- }
876
- renderPageNav() {
877
- const e = this.root.querySelector("#ds-page-nav");
878
- e && (e.innerHTML = this.renderData.dashboard.pages.map((t) => `
879
- <button class="ds-page-tab ${t.pageId === this.activePageId ? "ds-active" : ""}"
880
- data-page-id="${t.pageId}">${t.title}</button>
881
- `).join(""), e.querySelectorAll(".ds-page-tab").forEach((t) => {
882
- t.addEventListener("click", () => {
883
- var r, i;
884
- const s = t.dataset.pageId;
885
- s === this.activePageId && !this.isLoadingTab || this.triggerTabSwitch(s, (i = (r = this.getTabsForPage(s)[0]) == null ? void 0 : r.id) != null ? i : "");
886
- });
887
- }));
888
- }
889
- renderTabNav(e) {
890
- const t = this.root.querySelector("#ds-tab-nav");
891
- t && this.buildTabButtons(t, e);
892
- }
893
- buildTabButtons(e, t) {
894
- const s = this.getTabsForPage(t);
895
- if (s.length <= 1) {
896
- e.style.display = "none";
897
- return;
898
- }
899
- e.style.display = "", e.innerHTML = s.map((r) => `
900
- <button class="ds-page-tab ${r.id === this.activeTabId ? "ds-active" : ""}"
901
- data-tab-id="${r.id}" data-page-id="${t}">${r.title}</button>
902
- `).join(""), e.querySelectorAll(".ds-page-tab").forEach((r) => {
903
- r.addEventListener("click", () => {
904
- const i = r.dataset.tabId, a = r.dataset.pageId;
905
- i === this.activeTabId && a === this.activePageId && !this.isLoadingTab || this.triggerTabSwitch(a, i);
906
- });
907
- });
908
- }
909
- triggerTabSwitch(e, t) {
910
- this.isLoadingTab || !e || !t || (this.isLoadingTab = !0, this.activePageId = e, this.activeTabId = t, this.syncNavHighlight(), this.showTabSpinner(), this.opts.onTabSwitch(e, t));
911
- }
912
- syncNavHighlight() {
913
- this.root.querySelectorAll("#ds-page-nav .ds-page-tab").forEach((t) => {
914
- t.classList.toggle("ds-active", t.dataset.pageId === this.activePageId);
915
- });
916
- const e = this.root.querySelector("#ds-tab-nav");
917
- e && this.buildTabButtons(e, this.activePageId);
918
- }
919
- // ── Global Filter bar ─────────────────────────────────────────────────────
920
- // Merges global filters (from renderData.filters[]) with inline-filter
921
- // components (type='inline-filter' in renderData.components[]).
922
- // Inline-filter components are NOT rendered in the grid — they appear here.
923
- renderFilterBar() {
924
- if (this.opts.hideGlobalFilters) return;
925
- const e = [
926
- ...this.renderData.filters || [],
927
- ...this.inlineFiltersAsDefinitions()
928
- ];
929
- this.filterRenderer.render(e, this.activeFilters), this.fetchFilterOptions();
930
- }
931
- updateFilterBar() {
932
- if (this.opts.hideGlobalFilters) return;
933
- const e = [
934
- ...this.renderData.filters || [],
935
- ...this.inlineFiltersAsDefinitions()
936
- ];
937
- this.filterRenderer.render(e, this.activeFilters), this.fetchFilterOptions();
938
- }
939
- /** Convert inline-filter ComponentConfig entries to FilterDefinition shape */
940
- inlineFiltersAsDefinitions() {
941
- return this.opts.hideInlineFilters ? [] : (this.renderData.components || []).filter((e) => e.type === "inline-filter").map((e) => {
942
- var t, s, r, i, a, o, c, d;
943
- return {
944
- filterId: e.id,
945
- label: ((t = e.properties) == null ? void 0 : t.filterLabel) || e.title || "",
946
- type: ((s = e.properties) == null ? void 0 : s.filterType) || "dropdown",
947
- options: ((r = e.properties) == null ? void 0 : r.filterOptions) || ((i = e.properties) == null ? void 0 : i.options) || [],
948
- defaultValue: (a = e.properties) == null ? void 0 : a.defaultValue,
949
- targetComponents: ((o = e.properties) == null ? void 0 : o.targetComponents) || [],
950
- filterOperator: (c = e.properties) == null ? void 0 : c.filterOperator,
951
- applyToField: ((d = e.properties) == null ? void 0 : d.applyToField) || e.id
952
- };
953
- });
954
- }
955
- async fetchFilterOptions() {
956
- if (this.opts.onFetchFilterOptions)
957
- for (const e of this.renderData.filters) {
958
- if (e.type !== "dropdown" && e.type !== "multi-select" || e.options && e.options.length > 0) continue;
959
- const t = await this.opts.onFetchFilterOptions(e.filterId);
960
- t.length > 0 && this.filterRenderer.updateOptions(e.filterId, t);
961
- }
962
- }
963
- // ── Grid ──────────────────────────────────────────────────────────────────
964
- renderGrid() {
965
- this.hideTabSpinner();
966
- const e = this.root.querySelector("#ds-grid");
967
- if (!e) return;
968
- if (!this.opts.hideBackground) {
969
- const d = this.root.querySelector("#ds-canvas"), f = this.renderData.dashboard.pages.find(
970
- (u) => u.pageId === this.activePageId
971
- ), g = (f == null ? void 0 : f.backgroundColor) || "";
972
- d && (d.style.background = g), this.root.style.background = g;
973
- }
974
- this.chartRenderers.forEach((d) => d.destroy()), this.chartRenderers.clear();
975
- const t = (this.renderData.components || []).filter(
976
- (d) => d.type !== "inline-filter"
977
- );
978
- if (!t.length) {
979
- e.style.height = "200px", e.innerHTML = '<div class="ds-chart-empty" style="padding-top:80px;">No components on this tab.</div>';
980
- return;
981
- }
982
- const s = e.clientWidth || this.root.clientWidth || 800, r = t.map((d) => d.position), i = r.length ? Math.min(...r.map((d) => d.y)) : 0, a = new Q(s, i);
983
- e.style.height = `${Q.totalHeight(r)}px`, e.innerHTML = "";
984
- const o = 36, c = [];
985
- t.forEach((d) => {
986
- var b, l, m;
987
- const f = document.createElement("div");
988
- if (f.className = "ds-component-wrapper", f.dataset.componentId = d.id, a.applyStyles(f, d.position), !this.opts.hideBackground) {
989
- const p = ((l = (b = d.properties) == null ? void 0 : b.styleConfig) == null ? void 0 : l.backgroundColor) || ((m = d.properties) == null ? void 0 : m.backgroundColor);
990
- p && (f.style.background = p);
991
- }
992
- e.appendChild(f);
993
- const g = a.toPx(d.position), u = Math.max(50, g.height - o), n = document.createElement("div");
994
- n.style.cssText = "width:100%;height:100%;", f.appendChild(n);
995
- const h = this.getComponentDescription(d);
996
- if (h) {
997
- const p = this.getTooltipEl();
998
- f.addEventListener("mouseenter", () => {
999
- p.textContent = h, p.style.display = "block";
1000
- const y = f.getBoundingClientRect(), $ = p.offsetHeight || 36;
1001
- window.innerHeight - y.bottom >= $ + 6 ? p.style.top = `${y.bottom + 4}px` : p.style.top = `${y.top - $ - 4}px`, p.style.left = `${y.left}px`, p.style.width = `${y.width}px`;
1002
- }), f.addEventListener("mouseleave", () => {
1003
- p.style.display = "none";
1004
- });
1005
- }
1006
- c.push(
1007
- this.renderComponent(n, d, u).catch((p) => {
1008
- console.error(`[Deepspot SDK] Failed to render component ${d.id} (${d.type}):`, p), n.innerHTML = `<div style="padding:8px;color:#ef4444;font-size:12px;">⚠ ${d.type} render error</div>`;
1009
- })
1010
- );
1011
- }), Promise.all(c).catch(() => {
1012
- });
1013
- }
1014
- // ── Component renderer switch ─────────────────────────────────────────────
1015
- async renderComponent(e, t, s) {
1016
- var a, o, c, d, f, g, u, n, h, b, l, m, p, y, $, C, I, L, R, P, O, H, M, N, B, K, q, G, U, W, Y, _, J, V, X;
1017
- const r = this.renderData.data[t.id] || [], i = this.opts.theme;
1018
- switch (t.type) {
1019
- case "bar":
1020
- case "line":
1021
- case "pie":
1022
- case "donut":
1023
- case "area":
1024
- case "scatter":
1025
- case "stacked-bar": {
1026
- const w = new se(e);
1027
- await w.render(t, r, i, s), this.chartRenderers.set(t.id, w);
1028
- break;
1029
- }
1030
- case "table": {
1031
- const w = this.opts.onFetchTablePage ? (v, T) => this.opts.onFetchTablePage(t.id, v, T) : void 0;
1032
- this.tableRenderer.render(e, t, r, w);
1033
- break;
1034
- }
1035
- case "number-card":
1036
- this.cardRenderer.render(e, t, r);
1037
- break;
1038
- case "text-heading": {
1039
- if (this.opts.hideText) break;
1040
- const w = ((o = (a = t.properties) == null ? void 0 : a.data) == null ? void 0 : o.dataContent) || ((c = t.properties) == null ? void 0 : c.content) || t.title || "", v = ((d = t.properties) == null ? void 0 : d.styleConfig) || {}, T = this.scToInlineStyle(v), x = this.opts.hideBackground ? {} : this.parseTwColor((g = (f = t.properties) == null ? void 0 : f.color) != null ? g : ""), S = [[
1041
- x.bg ? `background:${x.bg}` : "",
1042
- x.text ? `color:${x.text}` : ""
1043
- ].filter(Boolean).join(";"), T].filter(Boolean).join(";");
1044
- e.innerHTML = `<div class="ds-text-heading" style="${S}">${w}</div>`;
1045
- break;
1046
- }
1047
- case "text-subheading": {
1048
- if (this.opts.hideText) break;
1049
- const w = ((n = (u = t.properties) == null ? void 0 : u.data) == null ? void 0 : n.dataContent) || ((h = t.properties) == null ? void 0 : h.content) || t.title || "", v = ((b = t.properties) == null ? void 0 : b.styleConfig) || {}, T = this.scToInlineStyle(v), x = this.opts.hideBackground ? {} : this.parseTwColor((m = (l = t.properties) == null ? void 0 : l.color) != null ? m : ""), S = [[
1050
- x.bg ? `background:${x.bg}` : "",
1051
- x.text ? `color:${x.text}` : ""
1052
- ].filter(Boolean).join(";"), T].filter(Boolean).join(";");
1053
- e.innerHTML = `<div class="ds-text-subheading" style="${S}">${w}</div>`;
1054
- break;
1055
- }
1056
- case "text-body": {
1057
- if (this.opts.hideText) break;
1058
- const w = ((y = (p = t.properties) == null ? void 0 : p.data) == null ? void 0 : y.dataContent) || (($ = t.properties) == null ? void 0 : $.content) || "", v = ((C = t.properties) == null ? void 0 : C.styleConfig) || {}, T = this.scToInlineStyle(v), x = this.opts.hideBackground ? {} : this.parseTwColor((L = (I = t.properties) == null ? void 0 : I.color) != null ? L : ""), S = [[
1059
- x.bg ? `background:${x.bg}` : "",
1060
- x.text ? `color:${x.text}` : ""
1061
- ].filter(Boolean).join(";"), T].filter(Boolean).join(";");
1062
- e.innerHTML = `<div class="ds-text-body" style="${S}">${w}</div>`;
1063
- break;
1064
- }
1065
- case "text-box": {
1066
- if (this.opts.hideText) break;
1067
- const w = ((P = (R = t.properties) == null ? void 0 : R.data) == null ? void 0 : P.dataContent) || ((O = t.properties) == null ? void 0 : O.content) || "", v = ((H = t.properties) == null ? void 0 : H.styleConfig) || {}, T = this.scToInlineStyle(v), x = this.opts.hideBackground ? {} : this.parseTwColor((N = (M = t.properties) == null ? void 0 : M.color) != null ? N : ""), S = [[
1068
- x.bg ? `background:${x.bg}` : "",
1069
- x.text ? `color:${x.text}` : ""
1070
- ].filter(Boolean).join(";"), T].filter(Boolean).join(";");
1071
- e.innerHTML = `<div class="ds-text-box" style="${S}">${w}</div>`;
1072
- break;
1073
- }
1074
- case "text": {
1075
- if (this.opts.hideText) break;
1076
- const w = ((K = (B = t.properties) == null ? void 0 : B.data) == null ? void 0 : K.dataContent) || ((q = t.properties) == null ? void 0 : q.content) || "", v = ((G = t.properties) == null ? void 0 : G.styleConfig) || {}, T = this.scToInlineStyle(v), x = this.opts.hideBackground ? {} : this.parseTwColor((W = (U = t.properties) == null ? void 0 : U.color) != null ? W : ""), S = [[
1077
- x.bg ? `background:${x.bg}` : "",
1078
- x.text ? `color:${x.text}` : ""
1079
- ].filter(Boolean).join(";"), T].filter(Boolean).join(";");
1080
- e.innerHTML = `<div class="ds-text-body" style="${S}">${w}</div>`;
1081
- break;
1082
- }
1083
- case "header": {
1084
- if (this.opts.hideHeader) break;
1085
- const w = ((_ = (Y = t.properties) == null ? void 0 : Y.data) == null ? void 0 : _.dataContent) || ((J = t.properties) == null ? void 0 : J.content) || t.title || "", v = ((V = t.properties) == null ? void 0 : V.styleConfig) || {}, T = !this.opts.hideBackground && v.backgroundColor ? `background:${v.backgroundColor};` : "", x = v.color ? `color:${v.color};` : v.textColor ? `color:${v.textColor};` : "";
1086
- e.innerHTML = `
1087
- <div class="ds-header-component" style="${T}${x}">
1088
- <div class="ds-header-content">${w}</div>
1089
- </div>`;
1090
- break;
1091
- }
1092
- case "section": {
1093
- const w = ((X = t.properties) == null ? void 0 : X.styleConfig) || {}, v = !this.opts.hideBackground && w.backgroundColor ? `background:${w.backgroundColor};` : "";
1094
- e.innerHTML = `
1095
- <div class="ds-section-wrapper" style="${v}">
1096
- ${t.title ? `<div class="ds-section-title">${t.title}</div>` : ""}
1097
- </div>`;
1098
- break;
1099
- }
1100
- }
1101
- }
1102
- // ── Spinner ───────────────────────────────────────────────────────────────
1103
- showTabSpinner() {
1104
- const e = this.root.querySelector("#ds-tab-loading"), t = this.root.querySelector("#ds-grid");
1105
- e && (e.style.display = "flex"), t && (t.style.opacity = "0.35");
1106
- }
1107
- hideTabSpinner() {
1108
- const e = this.root.querySelector("#ds-tab-loading"), t = this.root.querySelector("#ds-grid");
1109
- e && (e.style.display = "none"), t && (t.style.opacity = "1");
1110
- }
1111
- // ── Helpers ───────────────────────────────────────────────────────────────
1112
- /**
1113
- * Convert a styleConfig object to an inline CSS string.
1114
- * Maps camelCase keys (e.g. fontSize) to kebab-case CSS properties.
1115
- * Skips backgroundColor — that is applied to the wrapper div separately
1116
- * (so it can support CSS gradients via `background` shorthand).
1117
- */
1118
- scToInlineStyle(e) {
1119
- const t = /* @__PURE__ */ new Set(["backgroundColor"]), s = (r) => r.replace(/([A-Z])/g, "-$1").toLowerCase();
1120
- return Object.entries(e).filter(([r, i]) => !t.has(r) && i != null && i !== "").map(([r, i]) => `${s(r)}:${i}`).join(";");
1121
- }
1122
- /**
1123
- * Parse a Tailwind utility class string (e.g. "bg-violet-100 text-violet-600")
1124
- * into plain CSS hex values. Used for text components whose builder color
1125
- * is stored as Tailwind class names rather than raw CSS.
1126
- */
1127
- parseTwColor(e) {
1128
- if (!e) return {};
1129
- const t = {};
1130
- for (const s of e.split(/\s+/)) {
1131
- if (!s) continue;
1132
- if (s === "bg-white") {
1133
- t.bg = "#ffffff";
1134
- continue;
1135
- }
1136
- if (s === "bg-black") {
1137
- t.bg = "#000000";
1138
- continue;
1139
- }
1140
- if (s === "bg-transparent") {
1141
- t.bg = "transparent";
1142
- continue;
1143
- }
1144
- if (s === "text-white") {
1145
- t.text = "#ffffff";
1146
- continue;
1147
- }
1148
- if (s === "text-black") {
1149
- t.text = "#000000";
1150
- continue;
1151
- }
1152
- const r = /^(bg|text)-([a-z]+)-(\d+)$/.exec(s);
1153
- if (!r) continue;
1154
- const i = E.TW_PALETTE[r[2]], a = i == null ? void 0 : i[Number(r[3])];
1155
- a && (r[1] === "bg" && (t.bg = a), r[1] === "text" && (t.text = a));
1156
- }
1157
- return t;
1158
- }
1159
- getTooltipEl() {
1160
- if (!this.tooltipEl) {
1161
- const e = document.createElement("div");
1162
- e.className = "ds-global-tooltip", e.style.display = "none", document.body.appendChild(e), this.tooltipEl = e;
1163
- }
1164
- return this.tooltipEl;
1165
- }
1166
- getComponentDescription(e) {
1167
- var t, s, r, i;
1168
- if ((t = e.properties) != null && t.description) return String(e.properties.description);
1169
- try {
1170
- const a = (r = (s = e.properties) == null ? void 0 : s.data) == null ? void 0 : r.dataContent;
1171
- if (a) {
1172
- const o = typeof a == "string" ? JSON.parse(a) : a;
1173
- return (o == null ? void 0 : o.description) || ((i = o == null ? void 0 : o.config) == null ? void 0 : i.description) || "";
1174
- }
1175
- } catch (a) {
1176
- }
1177
- return "";
1178
- }
1179
- getTabsForPage(e) {
1180
- var t, s;
1181
- return (s = (t = this.renderData.dashboard.pages.find((r) => r.pageId === e)) == null ? void 0 : t.tabs) != null ? s : [];
1182
- }
1183
- };
1184
- E.TW_PALETTE = {
1185
- slate: { 50: "#f8fafc", 100: "#f1f5f9", 200: "#e2e8f0", 300: "#cbd5e1", 400: "#94a3b8", 500: "#64748b", 600: "#475569", 700: "#334155", 800: "#1e293b", 900: "#0f172a" },
1186
- gray: { 50: "#f9fafb", 100: "#f3f4f6", 200: "#e5e7eb", 300: "#d1d5db", 400: "#9ca3af", 500: "#6b7280", 600: "#4b5563", 700: "#374151", 800: "#1f2937", 900: "#111827" },
1187
- zinc: { 50: "#fafafa", 100: "#f4f4f5", 200: "#e4e4e7", 300: "#d4d4d8", 400: "#a1a1aa", 500: "#71717a", 600: "#52525b", 700: "#3f3f46", 800: "#27272a", 900: "#18181b" },
1188
- neutral: { 50: "#fafafa", 100: "#f5f5f5", 200: "#e5e5e5", 300: "#d4d4d4", 400: "#a3a3a3", 500: "#737373", 600: "#525252", 700: "#404040", 800: "#262626", 900: "#171717" },
1189
- stone: { 50: "#fafaf9", 100: "#f5f5f4", 200: "#e7e5e4", 300: "#d6d3d1", 400: "#a8a29e", 500: "#78716c", 600: "#57534e", 700: "#44403c", 800: "#292524", 900: "#1c1917" },
1190
- red: { 50: "#fef2f2", 100: "#fee2e2", 200: "#fecaca", 300: "#fca5a5", 400: "#f87171", 500: "#ef4444", 600: "#dc2626", 700: "#b91c1c", 800: "#991b1b", 900: "#7f1d1d" },
1191
- orange: { 50: "#fff7ed", 100: "#ffedd5", 200: "#fed7aa", 300: "#fdba74", 400: "#fb923c", 500: "#f97316", 600: "#ea580c", 700: "#c2410c", 800: "#9a3412", 900: "#7c2d12" },
1192
- amber: { 50: "#fffbeb", 100: "#fef3c7", 200: "#fde68a", 300: "#fcd34d", 400: "#fbbf24", 500: "#f59e0b", 600: "#d97706", 700: "#b45309", 800: "#92400e", 900: "#78350f" },
1193
- yellow: { 50: "#fefce8", 100: "#fef9c3", 200: "#fef08a", 300: "#fde047", 400: "#facc15", 500: "#eab308", 600: "#ca8a04", 700: "#a16207", 800: "#854d0e", 900: "#713f12" },
1194
- lime: { 50: "#f7fee7", 100: "#ecfccb", 200: "#d9f99d", 300: "#bef264", 400: "#a3e635", 500: "#84cc16", 600: "#65a30d", 700: "#4d7c0f", 800: "#3f6212", 900: "#365314" },
1195
- green: { 50: "#f0fdf4", 100: "#dcfce7", 200: "#bbf7d0", 300: "#86efac", 400: "#4ade80", 500: "#22c55e", 600: "#16a34a", 700: "#15803d", 800: "#166534", 900: "#14532d" },
1196
- emerald: { 50: "#ecfdf5", 100: "#d1fae5", 200: "#a7f3d0", 300: "#6ee7b7", 400: "#34d399", 500: "#10b981", 600: "#059669", 700: "#047857", 800: "#065f46", 900: "#064e3b" },
1197
- teal: { 50: "#f0fdfa", 100: "#ccfbf1", 200: "#99f6e4", 300: "#5eead4", 400: "#2dd4bf", 500: "#14b8a6", 600: "#0d9488", 700: "#0f766e", 800: "#115e59", 900: "#134e4a" },
1198
- cyan: { 50: "#ecfeff", 100: "#cffafe", 200: "#a5f3fc", 300: "#67e8f9", 400: "#22d3ee", 500: "#06b6d4", 600: "#0891b2", 700: "#0e7490", 800: "#155e75", 900: "#164e63" },
1199
- sky: { 50: "#f0f9ff", 100: "#e0f2fe", 200: "#bae6fd", 300: "#7dd3fc", 400: "#38bdf8", 500: "#0ea5e9", 600: "#0284c7", 700: "#0369a1", 800: "#075985", 900: "#0c4a6e" },
1200
- blue: { 50: "#eff6ff", 100: "#dbeafe", 200: "#bfdbfe", 300: "#93c5fd", 400: "#60a5fa", 500: "#3b82f6", 600: "#2563eb", 700: "#1d4ed8", 800: "#1e40af", 900: "#1e3a8a" },
1201
- indigo: { 50: "#eef2ff", 100: "#e0e7ff", 200: "#c7d2fe", 300: "#a5b4fc", 400: "#818cf8", 500: "#6366f1", 600: "#4f46e5", 700: "#4338ca", 800: "#3730a3", 900: "#312e81" },
1202
- violet: { 50: "#f5f3ff", 100: "#ede9fe", 200: "#ddd6fe", 300: "#c4b5fd", 400: "#a78bfa", 500: "#8b5cf6", 600: "#7c3aed", 700: "#6d28d9", 800: "#5b21b6", 900: "#4c1d95" },
1203
- purple: { 50: "#faf5ff", 100: "#f3e8ff", 200: "#e9d5ff", 300: "#d8b4fe", 400: "#c084fc", 500: "#a855f7", 600: "#9333ea", 700: "#7e22ce", 800: "#6b21a8", 900: "#581c87" },
1204
- fuchsia: { 50: "#fdf4ff", 100: "#fae8ff", 200: "#f5d0fe", 300: "#f0abfc", 400: "#e879f9", 500: "#d946ef", 600: "#c026d3", 700: "#a21caf", 800: "#86198f", 900: "#701a75" },
1205
- pink: { 50: "#fdf2f8", 100: "#fce7f3", 200: "#fbcfe8", 300: "#f9a8d4", 400: "#f472b6", 500: "#ec4899", 600: "#db2777", 700: "#be185d", 800: "#9d174d", 900: "#831843" },
1206
- rose: { 50: "#fff1f2", 100: "#ffe4e6", 200: "#fecdd3", 300: "#fda4af", 400: "#fb7185", 500: "#f43f5e", 600: "#e11d48", 700: "#be123c", 800: "#9f1239", 900: "#881337" }
1207
- };
1208
- let j = E;
1209
- class le {
1210
- constructor(e, t) {
1211
- this.chartRenderer = null, this.tableRenderer = new re(), this.cardRenderer = new ie(), this.root = e, this.theme = t;
1212
- }
1213
- render(e, t) {
1214
- const s = e.components[0];
1215
- if (!s) {
1216
- this.root.innerHTML = `<div class="ds-embed-error">
1217
- <div class="ds-embed-error-icon">⚠</div>
1218
- <div>Component not found.</div>
1219
- </div>`;
1220
- return;
1221
- }
1222
- const r = e.data[s.id] || [];
1223
- this.renderComponent(s, r), t == null || t();
1224
- }
1225
- update(e) {
1226
- const t = e.components[0];
1227
- if (!t) return;
1228
- const s = e.data[t.id] || [];
1229
- switch (t.type) {
1230
- case "bar":
1231
- case "line":
1232
- case "pie":
1233
- case "donut":
1234
- case "area":
1235
- this.chartRenderer ? this.chartRenderer.update(t, s, this.theme) : this.renderComponent(t, s);
1236
- break;
1237
- case "table":
1238
- this.tableRenderer.render(this.root, t, s);
1239
- break;
1240
- case "number-card":
1241
- this.cardRenderer.render(this.root, t, s);
1242
- break;
1243
- }
1244
- }
1245
- destroy() {
1246
- var e;
1247
- (e = this.chartRenderer) == null || e.destroy(), this.chartRenderer = null, this.root.innerHTML = "";
1248
- }
1249
- renderComponent(e, t) {
1250
- var s;
1251
- switch (e.type) {
1252
- case "bar":
1253
- case "line":
1254
- case "pie":
1255
- case "donut":
1256
- case "area":
1257
- (s = this.chartRenderer) == null || s.destroy(), this.chartRenderer = new se(this.root), this.chartRenderer.render(e, t, this.theme);
1258
- break;
1259
- case "table":
1260
- this.tableRenderer.render(this.root, e, t);
1261
- break;
1262
- case "number-card":
1263
- this.cardRenderer.render(this.root, e, t);
1264
- break;
1265
- default:
1266
- this.root.innerHTML = `<div class="ds-chart-empty">Unsupported component type: ${e.type}</div>`;
1267
- }
1268
- }
1269
- }
1270
- let ee = !1;
1271
- function ce() {
1272
- if (!(ee || typeof document == "undefined")) {
1273
- ee = !0;
1274
- try {
1275
- const k = `/* ─────────────────────────────────────────────────────────────────────────────
1276
- Deepspot SDK — Base Embed Styles
1277
- Injected once into the host page's <head> by the SDK.
1278
- All selectors are scoped under .ds-embed-* to avoid leaking into host styles.
1279
- ───────────────────────────────────────────────────────────────────────────── */
1280
-
1281
- /* ── Reset inside SDK containers ──────────────────────────────────────────── */
1282
- .ds-embed-root *,
1283
- .ds-embed-root *::before,
1284
- .ds-embed-root *::after {
1285
- box-sizing: border-box;
1286
- margin: 0;
1287
- padding: 0;
1288
- }
1289
-
1290
- /* ── Root container ────────────────────────────────────────────────────────── */
1291
- .ds-embed-root {
1292
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
1293
- Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
1294
- font-size: 14px;
1295
- line-height: 1.5;
1296
- width: 100%;
1297
- height: 100%;
1298
- overflow: auto;
1299
- position: relative;
1300
- /* Default white background — overridden by page backgroundColor from publish page */
1301
- background: #ffffff;
1302
- color: #111827;
1303
- }
1304
-
1305
- .ds-embed-root.ds-theme-light {
1306
- /* No forced background — let component styleConfig backgrounds show through */
1307
- color: #111827;
1308
- }
1309
-
1310
- .ds-embed-root.ds-theme-dark {
1311
- /* No forced background — let component styleConfig backgrounds show through */
1312
- color: #f1f5f9;
1313
- }
1314
-
1315
- /* ── Loading state ─────────────────────────────────────────────────────────── */
1316
- .ds-embed-loading {
1317
- display: flex;
1318
- flex-direction: column;
1319
- align-items: center;
1320
- justify-content: center;
1321
- height: 100%;
1322
- min-height: 200px;
1323
- gap: 12px;
1324
- color: #6b7280;
1325
- }
1326
-
1327
- .ds-embed-spinner {
1328
- width: 36px;
1329
- height: 36px;
1330
- border: 3px solid #e5e7eb;
1331
- border-top-color: #6366f1;
1332
- border-radius: 50%;
1333
- animation: ds-spin 0.8s linear infinite;
1334
- }
1335
-
1336
- @keyframes ds-spin {
1337
- to { transform: rotate(360deg); }
1338
- }
1339
-
1340
- .ds-embed-loading-text {
1341
- font-size: 13px;
1342
- }
1343
-
1344
- /* ── Error state ───────────────────────────────────────────────────────────── */
1345
- .ds-embed-error {
1346
- display: flex;
1347
- flex-direction: column;
1348
- align-items: center;
1349
- justify-content: center;
1350
- height: 100%;
1351
- min-height: 200px;
1352
- gap: 8px;
1353
- padding: 24px;
1354
- text-align: center;
1355
- color: #ef4444;
1356
- }
1357
-
1358
- .ds-embed-error-icon {
1359
- font-size: 32px;
1360
- }
1361
-
1362
- .ds-embed-error-message {
1363
- font-size: 13px;
1364
- color: #6b7280;
1365
- }
1366
-
1367
- /* ── Filter bar ────────────────────────────────────────────────────────────── */
1368
- .ds-filter-bar {
1369
- display: flex;
1370
- flex-wrap: wrap;
1371
- align-items: center;
1372
- gap: 10px;
1373
- padding: 10px 16px;
1374
- border-bottom: 1px solid #e5e7eb;
1375
- background: inherit;
1376
- }
1377
-
1378
- .ds-theme-dark .ds-filter-bar {
1379
- border-bottom-color: #1e293b;
1380
- }
1381
-
1382
- .ds-filter-item {
1383
- display: flex;
1384
- flex-direction: column;
1385
- gap: 4px;
1386
- }
1387
-
1388
- .ds-filter-label {
1389
- font-size: 11px;
1390
- font-weight: 600;
1391
- text-transform: uppercase;
1392
- letter-spacing: 0.05em;
1393
- color: #6b7280;
1394
- }
1395
-
1396
- .ds-filter-select,
1397
- .ds-filter-input {
1398
- height: 32px;
1399
- padding: 0 10px;
1400
- border: 1px solid #d1d5db;
1401
- border-radius: 6px;
1402
- font-size: 13px;
1403
- background: #ffffff;
1404
- color: #111827;
1405
- cursor: pointer;
1406
- min-width: 130px;
1407
- }
1408
-
1409
- .ds-theme-dark .ds-filter-select,
1410
- .ds-theme-dark .ds-filter-input {
1411
- background: #1e293b;
1412
- border-color: #334155;
1413
- color: #f1f5f9;
1414
- }
1415
-
1416
- .ds-filter-select:focus,
1417
- .ds-filter-input:focus {
1418
- outline: none;
1419
- border-color: #6366f1;
1420
- box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
1421
- }
1422
-
1423
- .ds-date-range-inputs {
1424
- display: flex;
1425
- gap: 6px;
1426
- align-items: center;
1427
- }
1428
-
1429
- .ds-date-range-inputs input[type="date"] {
1430
- height: 32px;
1431
- padding: 0 8px;
1432
- border: 1px solid #d1d5db;
1433
- border-radius: 6px;
1434
- font-size: 13px;
1435
- background: #ffffff;
1436
- color: #111827;
1437
- }
1438
-
1439
- .ds-theme-dark .ds-date-range-inputs input[type="date"] {
1440
- background: #1e293b;
1441
- border-color: #334155;
1442
- color: #f1f5f9;
1443
- }
1444
-
1445
- /* ── Page / Tab navigation ─────────────────────────────────────────────────── */
1446
- .ds-page-nav {
1447
- display: flex;
1448
- gap: 4px;
1449
- padding: 8px 16px 0;
1450
- border-bottom: 1px solid #e5e7eb;
1451
- overflow-x: auto;
1452
- }
1453
-
1454
- .ds-theme-dark .ds-page-nav {
1455
- border-bottom-color: #1e293b;
1456
- }
1457
-
1458
- .ds-page-tab {
1459
- padding: 6px 16px;
1460
- font-size: 13px;
1461
- font-weight: 500;
1462
- border: none;
1463
- border-bottom: 2px solid transparent;
1464
- background: transparent;
1465
- color: #6b7280;
1466
- cursor: pointer;
1467
- white-space: nowrap;
1468
- transition: color 0.15s, border-color 0.15s;
1469
- }
1470
-
1471
- .ds-page-tab:hover {
1472
- color: #111827;
1473
- }
1474
-
1475
- .ds-theme-dark .ds-page-tab:hover {
1476
- color: #f1f5f9;
1477
- }
1478
-
1479
- .ds-page-tab.ds-active {
1480
- color: #6366f1;
1481
- border-bottom-color: #6366f1;
1482
- }
1483
-
1484
- /* ── Grid canvas ───────────────────────────────────────────────────────────── */
1485
- .ds-canvas {
1486
- position: relative;
1487
- width: 100%;
1488
- /* No horizontal padding — grid.clientWidth must equal container width so
1489
- GridLayout.colWidth = containerWidth/24 matches the dashboard builder.
1490
- 16px on each side was shrinking the grid by 32px and misaligning positions. */
1491
- padding: 8px 0 16px;
1492
- overflow-x: hidden;
1493
- }
1494
-
1495
- .ds-grid {
1496
- position: relative;
1497
- width: 100%;
1498
- /* Clip absolutely-positioned children to the grid boundary so rounding
1499
- errors or miscalculated widths don't create a horizontal scrollbar. */
1500
- overflow: hidden;
1501
- }
1502
-
1503
- .ds-component-wrapper {
1504
- position: absolute;
1505
- /* overflow intentionally not set — individual card components handle their own clipping,
1506
- and tooltip bubbles must be able to overflow the wrapper boundary */
1507
- }
1508
-
1509
- /* ── Description tooltip — fixed on body, positioned via JS, never clips content ── */
1510
- .ds-global-tooltip {
1511
- position: fixed;
1512
- z-index: 9999;
1513
- background: rgba(15, 23, 42, 0.92);
1514
- color: #f1f5f9;
1515
- font-size: 12px;
1516
- line-height: 1.6;
1517
- padding: 8px 14px;
1518
- border-radius: 8px;
1519
- white-space: normal;
1520
- pointer-events: none;
1521
- backdrop-filter: blur(4px);
1522
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
1523
- max-width: 600px;
1524
- }
1525
-
1526
- /* ── Chart component ───────────────────────────────────────────────────────── */
1527
- .ds-chart-card {
1528
- width: 100%;
1529
- height: 100%;
1530
- background: #ffffff;
1531
- border: 1px solid #e5e7eb;
1532
- border-radius: 10px;
1533
- overflow: hidden;
1534
- display: flex;
1535
- flex-direction: column;
1536
- }
1537
-
1538
- .ds-theme-dark .ds-chart-card {
1539
- background: #1e293b;
1540
- border-color: #334155;
1541
- }
1542
-
1543
- .ds-chart-title {
1544
- padding: 10px 14px 4px;
1545
- font-size: 13px;
1546
- font-weight: 600;
1547
- color: #374151;
1548
- flex-shrink: 0;
1549
- }
1550
-
1551
- .ds-theme-dark .ds-chart-title {
1552
- color: #e2e8f0;
1553
- }
1554
-
1555
- .ds-chart-body {
1556
- flex: 1;
1557
- min-height: 0;
1558
- }
1559
-
1560
- .ds-chart-empty {
1561
- display: flex;
1562
- align-items: center;
1563
- justify-content: center;
1564
- height: 100%;
1565
- color: #9ca3af;
1566
- font-size: 13px;
1567
- }
1568
-
1569
- /* ── Table component ───────────────────────────────────────────────────────── */
1570
- .ds-table-card {
1571
- width: 100%;
1572
- height: 100%;
1573
- background: #ffffff;
1574
- border: 1px solid #e5e7eb;
1575
- border-radius: 10px;
1576
- overflow: hidden;
1577
- display: flex;
1578
- flex-direction: column;
1579
- position: relative;
1580
- }
1581
-
1582
- .ds-theme-dark .ds-table-card {
1583
- background: #1e293b;
1584
- border-color: #334155;
1585
- }
1586
-
1587
- /* ── Table header: dot + title on left, search on right ──────────────────────*/
1588
- .ds-table-header-row {
1589
- display: flex;
1590
- align-items: center;
1591
- justify-content: space-between;
1592
- padding: 10px 14px 8px;
1593
- flex-shrink: 0;
1594
- gap: 8px;
1595
- border-bottom: 1px solid #f3f4f6;
1596
- }
1597
-
1598
- .ds-theme-dark .ds-table-header-row {
1599
- border-bottom-color: #334155;
1600
- }
1601
-
1602
- .ds-table-title-wrap {
1603
- display: flex;
1604
- align-items: center;
1605
- gap: 6px;
1606
- min-width: 0;
1607
- }
1608
-
1609
- .ds-table-dot {
1610
- width: 8px;
1611
- height: 8px;
1612
- border-radius: 50%;
1613
- background: #6366f1;
1614
- flex-shrink: 0;
1615
- }
1616
-
1617
- .ds-table-title {
1618
- font-size: 13px;
1619
- font-weight: 600;
1620
- color: #374151;
1621
- white-space: nowrap;
1622
- overflow: hidden;
1623
- text-overflow: ellipsis;
1624
- }
1625
-
1626
- .ds-theme-dark .ds-table-title {
1627
- color: #e2e8f0;
1628
- }
1629
-
1630
- /* ── Search ──────────────────────────────────────────────────────────────────*/
1631
- .ds-table-search-wrap {
1632
- position: relative;
1633
- flex-shrink: 0;
1634
- }
1635
-
1636
- .ds-table-search-icon {
1637
- position: absolute;
1638
- left: 8px;
1639
- top: 50%;
1640
- transform: translateY(-50%);
1641
- width: 14px;
1642
- height: 14px;
1643
- color: #9ca3af;
1644
- pointer-events: none;
1645
- }
1646
-
1647
- .ds-table-search {
1648
- height: 30px;
1649
- padding: 0 10px 0 28px;
1650
- border: 1px solid #e5e7eb;
1651
- border-radius: 6px;
1652
- font-size: 12px;
1653
- background: #f9fafb;
1654
- color: #374151;
1655
- width: 180px;
1656
- outline: none;
1657
- transition: border-color 0.15s, box-shadow 0.15s;
1658
- }
1659
-
1660
- .ds-table-search:focus {
1661
- border-color: #6366f1;
1662
- box-shadow: 0 0 0 2px rgba(99,102,241,0.15);
1663
- background: #fff;
1664
- }
1665
-
1666
- .ds-theme-dark .ds-table-search {
1667
- background: #0f172a;
1668
- border-color: #334155;
1669
- color: #e2e8f0;
1670
- }
1671
-
1672
- .ds-theme-dark .ds-table-search:focus {
1673
- background: #1e293b;
1674
- }
1675
-
1676
- /* ── Scroll area ─────────────────────────────────────────────────────────────*/
1677
- .ds-table-scroll {
1678
- flex: 1;
1679
- overflow: auto;
1680
- min-height: 0;
1681
- }
1682
-
1683
- .ds-table {
1684
- width: 100%;
1685
- border-collapse: collapse;
1686
- font-size: 12px;
1687
- }
1688
-
1689
- .ds-table-sno-th {
1690
- width: 48px;
1691
- text-align: center !important;
1692
- }
1693
-
1694
- .ds-table-sno {
1695
- text-align: center;
1696
- color: #9ca3af;
1697
- font-size: 11px;
1698
- }
1699
-
1700
- .ds-theme-dark .ds-table-sno {
1701
- color: #64748b;
1702
- }
1703
-
1704
- .ds-table th {
1705
- position: sticky;
1706
- top: 0;
1707
- padding: 8px 12px;
1708
- text-align: left;
1709
- font-weight: 600;
1710
- font-size: 11px;
1711
- text-transform: uppercase;
1712
- letter-spacing: 0.05em;
1713
- background: #f9fafb;
1714
- color: #6b7280;
1715
- border-bottom: 1px solid #e5e7eb;
1716
- white-space: nowrap;
1717
- }
1718
-
1719
- .ds-theme-dark .ds-table th {
1720
- background: #0f172a;
1721
- color: #94a3b8;
1722
- border-bottom-color: #334155;
1723
- }
1724
-
1725
- .ds-table td {
1726
- padding: 8px 12px;
1727
- border-bottom: 1px solid #f3f4f6;
1728
- color: #374151;
1729
- max-width: 200px;
1730
- overflow: hidden;
1731
- text-overflow: ellipsis;
1732
- white-space: nowrap;
1733
- }
1734
-
1735
- .ds-theme-dark .ds-table td {
1736
- color: #cbd5e1;
1737
- border-bottom-color: #1e293b;
1738
- }
1739
-
1740
- .ds-table tr:hover td {
1741
- background: #f9fafb;
1742
- }
1743
-
1744
- .ds-theme-dark .ds-table tr:hover td {
1745
- background: #0f172a;
1746
- }
1747
-
1748
- /* ── Footer: info + pagination ───────────────────────────────────────────────*/
1749
- .ds-table-footer {
1750
- display: flex;
1751
- align-items: center;
1752
- justify-content: space-between;
1753
- padding: 8px 14px;
1754
- border-top: 1px solid #f3f4f6;
1755
- flex-shrink: 0;
1756
- gap: 8px;
1757
- flex-wrap: wrap;
1758
- }
1759
-
1760
- .ds-theme-dark .ds-table-footer {
1761
- border-top-color: #334155;
1762
- }
1763
-
1764
- .ds-table-info {
1765
- display: flex;
1766
- align-items: center;
1767
- gap: 8px;
1768
- font-size: 12px;
1769
- color: #6b7280;
1770
- }
1771
-
1772
- .ds-theme-dark .ds-table-info {
1773
- color: #94a3b8;
1774
- }
1775
-
1776
- .ds-table-rows-label {
1777
- margin-left: 4px;
1778
- }
1779
-
1780
- .ds-table-page-size {
1781
- height: 28px;
1782
- padding: 0 6px;
1783
- border: 1px solid #e5e7eb;
1784
- border-radius: 5px;
1785
- font-size: 12px;
1786
- background: #fff;
1787
- color: #374151;
1788
- cursor: pointer;
1789
- outline: none;
1790
- }
1791
-
1792
- .ds-theme-dark .ds-table-page-size {
1793
- background: #0f172a;
1794
- border-color: #334155;
1795
- color: #e2e8f0;
1796
- }
1797
-
1798
- .ds-table-pagination {
1799
- display: flex;
1800
- align-items: center;
1801
- gap: 6px;
1802
- }
1803
-
1804
- .ds-table-pg-btn {
1805
- height: 28px;
1806
- padding: 0 10px;
1807
- border: 1px solid #e5e7eb;
1808
- border-radius: 5px;
1809
- font-size: 12px;
1810
- font-weight: 500;
1811
- background: #fff;
1812
- color: #374151;
1813
- cursor: pointer;
1814
- transition: background 0.12s, border-color 0.12s;
1815
- white-space: nowrap;
1816
- }
1817
-
1818
- .ds-table-pg-btn:hover:not(:disabled) {
1819
- background: #f3f4f6;
1820
- border-color: #d1d5db;
1821
- }
1822
-
1823
- .ds-table-pg-btn:disabled {
1824
- opacity: 0.4;
1825
- cursor: not-allowed;
1826
- }
1827
-
1828
- .ds-theme-dark .ds-table-pg-btn {
1829
- background: #1e293b;
1830
- border-color: #334155;
1831
- color: #e2e8f0;
1832
- }
1833
-
1834
- .ds-theme-dark .ds-table-pg-btn:hover:not(:disabled) {
1835
- background: #0f172a;
1836
- }
1837
-
1838
- .ds-table-pg-info {
1839
- font-size: 12px;
1840
- color: #6b7280;
1841
- white-space: nowrap;
1842
- }
1843
-
1844
- .ds-theme-dark .ds-table-pg-info {
1845
- color: #94a3b8;
1846
- }
1847
-
1848
- /* ── Loading overlay while fetching a page ───────────────────────────────────*/
1849
- .ds-table-loading-overlay {
1850
- position: absolute;
1851
- inset: 0;
1852
- background: rgba(255,255,255,0.6);
1853
- display: flex;
1854
- align-items: center;
1855
- justify-content: center;
1856
- border-radius: 10px;
1857
- z-index: 10;
1858
- }
1859
-
1860
- .ds-theme-dark .ds-table-loading-overlay {
1861
- background: rgba(30,41,59,0.7);
1862
- }
1863
-
1864
- /* ── KPI Number Card ───────────────────────────────────────────────────────── */
1865
- .ds-card {
1866
- width: 100%;
1867
- height: 100%;
1868
- border: 1px solid #e5e7eb;
1869
- border-radius: 10px;
1870
- display: flex;
1871
- flex-direction: column;
1872
- align-items: center;
1873
- justify-content: center;
1874
- padding: 16px;
1875
- text-align: center;
1876
- background: #ffffff;
1877
- }
1878
-
1879
- .ds-theme-dark .ds-card {
1880
- background: #1e293b;
1881
- border-color: #334155;
1882
- }
1883
-
1884
- .ds-card-label {
1885
- font-size: 12px;
1886
- font-weight: 500;
1887
- color: #6b7280;
1888
- margin-bottom: 6px;
1889
- text-transform: uppercase;
1890
- letter-spacing: 0.05em;
1891
- }
1892
-
1893
- .ds-card-value {
1894
- font-size: 32px;
1895
- font-weight: 700;
1896
- color: #111827;
1897
- line-height: 1.1;
1898
- }
1899
-
1900
- .ds-theme-dark .ds-card-value {
1901
- color: #f1f5f9;
1902
- }
1903
-
1904
- /* ── v2: Text / content components ────────────────────────────────────────── */
1905
-
1906
- .ds-text-heading {
1907
- width: 100%;
1908
- height: 100%;
1909
- display: flex;
1910
- flex-direction: column;
1911
- justify-content: center;
1912
- padding: 8px 12px;
1913
- font-size: 20px;
1914
- font-weight: 700;
1915
- color: #111827;
1916
- line-height: 1.3;
1917
- overflow: hidden;
1918
- }
1919
-
1920
- .ds-theme-dark .ds-text-heading {
1921
- color: #f1f5f9;
1922
- }
1923
-
1924
- .ds-text-subheading {
1925
- width: 100%;
1926
- height: 100%;
1927
- display: flex;
1928
- flex-direction: column;
1929
- justify-content: center;
1930
- padding: 6px 12px;
1931
- font-size: 15px;
1932
- font-weight: 600;
1933
- color: #374151;
1934
- line-height: 1.4;
1935
- overflow: hidden;
1936
- }
1937
-
1938
- .ds-theme-dark .ds-text-subheading {
1939
- color: #cbd5e1;
1940
- }
1941
-
1942
- .ds-text-body {
1943
- width: 100%;
1944
- height: 100%;
1945
- padding: 8px 12px;
1946
- font-size: 14px;
1947
- color: #374151;
1948
- line-height: 1.6;
1949
- overflow: auto;
1950
- }
1951
-
1952
- .ds-theme-dark .ds-text-body {
1953
- color: #94a3b8;
1954
- }
1955
-
1956
- .ds-text-box {
1957
- width: 100%;
1958
- height: 100%;
1959
- padding: 10px 14px;
1960
- font-size: 13px;
1961
- color: #374151;
1962
- line-height: 1.6;
1963
- border: 1px solid #e5e7eb;
1964
- border-radius: 8px;
1965
- background: #f9fafb;
1966
- overflow: auto;
1967
- }
1968
-
1969
- .ds-theme-dark .ds-text-box {
1970
- color: #cbd5e1;
1971
- border-color: #334155;
1972
- background: #1e293b;
1973
- }
1974
-
1975
- /* ── v2: Header component ──────────────────────────────────────────────────── */
1976
-
1977
- .ds-header-component {
1978
- width: 100%;
1979
- height: 100%;
1980
- display: flex;
1981
- align-items: center;
1982
- padding: 12px 20px;
1983
- background: #f3f4f6;
1984
- border-bottom: 1px solid #e5e7eb;
1985
- overflow: hidden;
1986
- }
1987
-
1988
- .ds-theme-dark .ds-header-component {
1989
- background: #1e293b;
1990
- border-bottom-color: #334155;
1991
- }
1992
-
1993
- .ds-header-content {
1994
- font-size: 18px;
1995
- font-weight: 700;
1996
- color: #111827;
1997
- white-space: nowrap;
1998
- overflow: hidden;
1999
- text-overflow: ellipsis;
2000
- }
2001
-
2002
- .ds-theme-dark .ds-header-content {
2003
- color: #f1f5f9;
2004
- }
2005
-
2006
- /* ── v2: Section container ─────────────────────────────────────────────────── */
2007
-
2008
- .ds-section-wrapper {
2009
- width: 100%;
2010
- height: 100%;
2011
- border: 1px solid #e5e7eb;
2012
- border-radius: 10px;
2013
- background: #f9fafb;
2014
- overflow: hidden;
2015
- }
2016
-
2017
- .ds-theme-dark .ds-section-wrapper {
2018
- border-color: #334155;
2019
- background: #1e293b;
2020
- }
2021
-
2022
- .ds-section-title {
2023
- padding: 8px 14px;
2024
- font-size: 12px;
2025
- font-weight: 600;
2026
- text-transform: uppercase;
2027
- letter-spacing: 0.05em;
2028
- color: #6b7280;
2029
- border-bottom: 1px solid #e5e7eb;
2030
- }
2031
-
2032
- .ds-theme-dark .ds-section-title {
2033
- color: #94a3b8;
2034
- border-bottom-color: #334155;
2035
- }
2036
-
2037
- /* ── v2: Inline filter (selection-filter placed in-grid) ───────────────────── */
2038
-
2039
- .ds-inline-filter {
2040
- width: 100%;
2041
- height: 100%;
2042
- display: flex;
2043
- flex-direction: column;
2044
- justify-content: center;
2045
- gap: 4px;
2046
- padding: 6px 10px;
2047
- }
2048
-
2049
- .ds-inline-filter-select,
2050
- .ds-inline-filter-input {
2051
- width: 100%;
2052
- height: 34px;
2053
- padding: 0 10px;
2054
- border: 1px solid #d1d5db;
2055
- border-radius: 6px;
2056
- font-size: 13px;
2057
- background: #ffffff;
2058
- color: #111827;
2059
- cursor: pointer;
2060
- outline: none;
2061
- transition: border-color 0.15s, box-shadow 0.15s;
2062
- }
2063
-
2064
- .ds-inline-filter-select:focus,
2065
- .ds-inline-filter-input:focus {
2066
- border-color: #6366f1;
2067
- box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
2068
- }
2069
-
2070
- .ds-theme-dark .ds-inline-filter-select,
2071
- .ds-theme-dark .ds-inline-filter-input {
2072
- background: #1e293b;
2073
- border-color: #334155;
2074
- color: #f1f5f9;
2075
- }
2076
-
2077
- /* ── Export toolbar ────────────────────────────────────────────────────────── */
2078
- .ds-toolbar {
2079
- display: flex;
2080
- justify-content: flex-end;
2081
- padding: 8px 16px 0;
2082
- gap: 8px;
2083
- }
2084
-
2085
- .ds-toolbar-btn {
2086
- height: 30px;
2087
- padding: 0 12px;
2088
- border: 1px solid #d1d5db;
2089
- border-radius: 6px;
2090
- font-size: 12px;
2091
- font-weight: 500;
2092
- background: #ffffff;
2093
- color: #374151;
2094
- cursor: pointer;
2095
- display: flex;
2096
- align-items: center;
2097
- gap: 5px;
2098
- transition: background 0.15s, border-color 0.15s;
2099
- }
2100
-
2101
- .ds-toolbar-btn:hover {
2102
- background: #f9fafb;
2103
- border-color: #9ca3af;
2104
- }
2105
-
2106
- .ds-theme-dark .ds-toolbar-btn {
2107
- background: #1e293b;
2108
- border-color: #334155;
2109
- color: #e2e8f0;
2110
- }
2111
-
2112
- .ds-theme-dark .ds-toolbar-btn:hover {
2113
- background: #0f172a;
2114
- }
2115
- `, e = document.createElement("style");
2116
- e.id = "deepspot-sdk-styles", e.textContent = k, document.head.appendChild(e);
2117
- } catch (k) {
2118
- }
2119
- }
2120
- }
2121
- function te(k) {
2122
- if (typeof k == "string") {
2123
- const e = document.querySelector(k);
2124
- if (!e) throw new Error(`Deepspot SDK: container "${k}" not found in DOM`);
2125
- return e;
2126
- }
2127
- return k;
2128
- }
2129
- class he {
2130
- constructor(e) {
2131
- if (!e.apiKey) throw new Error("Deepspot SDK: apiKey is required");
2132
- if (!e.baseUrl) throw new Error("Deepspot SDK: baseUrl is required");
2133
- this.apiClient = new ae(e.baseUrl, e.apiKey), ce();
2134
- }
2135
- // ── Embed full dashboard ────────────────────────────────────────────────────
2136
- async embedDashboard(e) {
2137
- var a, o, c, d, f, g, u, n, h, b;
2138
- const t = te(e.container), s = (a = e.embedLevel) != null ? a : "dashboard", r = (o = e.theme) != null ? o : "light";
2139
- t.style.height = e.height || "600px", t.style.display = "block";
2140
- const i = this.createRoot(t, r);
2141
- this.showLoading(i);
2142
- try {
2143
- const l = await this.apiClient.getEmbedToken({
2144
- dashboardId: e.dashboardId,
2145
- embedType: "dashboard",
2146
- embedLevel: s,
2147
- userId: e.userId,
2148
- tenantId: e.tenantId
2149
- }), m = await this.apiClient.getDashboardRender(
2150
- e.dashboardId,
2151
- l,
2152
- {
2153
- embedLevel: s,
2154
- pageId: e.pageId,
2155
- tabId: e.tabId,
2156
- filters: e.filters || {}
2157
- }
2158
- );
2159
- let p;
2160
- const y = new j(i, {
2161
- embedLevel: s,
2162
- theme: r,
2163
- // v1 backward compat (hideFilters → hideGlobalFilters)
2164
- hideFilters: (c = e.hideGlobalFilters) != null ? c : !1,
2165
- hideExport: (d = e.hideExport) != null ? d : !1,
2166
- // v2 visibility
2167
- hideGlobalFilters: (f = e.hideGlobalFilters) != null ? f : !1,
2168
- hideInlineFilters: (g = e.hideInlineFilters) != null ? g : !1,
2169
- hideText: (u = e.hideText) != null ? u : !1,
2170
- hideHeader: (n = e.hideHeader) != null ? n : !1,
2171
- hideBackground: (h = e.hideBackground) != null ? h : !1,
2172
- initialFilters: e.filters || {},
2173
- onFilterChange: e.onFilterChange,
2174
- onReady: e.onReady,
2175
- onFetchFilterOptions: ($) => this.apiClient.getFilterOptions(e.dashboardId, $, l),
2176
- onFetchTablePage: ($, C, I) => this.apiClient.getTablePage(e.dashboardId, $, C, I, l),
2177
- onTabSwitch: async ($, C) => {
2178
- var I, L, R;
2179
- (I = e.onTabSwitch) == null || I.call(e, $, C);
2180
- try {
2181
- const P = await this.apiClient.getDashboardRender(
2182
- e.dashboardId,
2183
- l,
2184
- {
2185
- embedLevel: s,
2186
- pageId: $,
2187
- tabId: C,
2188
- filters: (R = (L = p == null ? void 0 : p.getActiveFilters()) != null ? L : e.filters) != null ? R : {}
2189
- }
2190
- );
2191
- y.update(P);
2192
- } catch (P) {
2193
- console.error("Deepspot SDK: tab fetch failed", P);
2194
- }
2195
- }
2196
- });
2197
- return y.render(m), p = new Z({
2198
- dashboardId: e.dashboardId,
2199
- token: l,
2200
- embedType: "dashboard",
2201
- embedLevel: s,
2202
- activePageId: m.activePage,
2203
- activeTabId: m.activeTab,
2204
- activeFilters: e.filters || {},
2205
- apiClient: this.apiClient,
2206
- renderer: y,
2207
- onFilterChange: e.onFilterChange
2208
- }), p;
2209
- } catch (l) {
2210
- throw this.showError(i, (l == null ? void 0 : l.message) || "Failed to load dashboard"), (b = e.onError) == null || b.call(e, (l == null ? void 0 : l.message) || "Failed to load dashboard"), l;
2211
- }
2212
- }
2213
- // ── Embed single report (component) ────────────────────────────────────────
2214
- async embedReport(e) {
2215
- var r;
2216
- const t = te(e.container);
2217
- t.style.height = e.height || "400px", t.style.display = "block";
2218
- const s = this.createRoot(t, e.theme || "light");
2219
- this.showLoading(s);
2220
- try {
2221
- const i = await this.apiClient.getEmbedToken({
2222
- dashboardId: e.dashboardId,
2223
- embedType: "report",
2224
- componentId: e.componentId,
2225
- userId: e.userId,
2226
- tenantId: e.tenantId
2227
- }), a = await this.apiClient.getReportRender(
2228
- e.dashboardId,
2229
- e.componentId,
2230
- i,
2231
- { filters: e.filters || {} }
2232
- ), o = new le(s, e.theme || "light");
2233
- return o.render(a, e.onReady), new Z({
2234
- dashboardId: e.dashboardId,
2235
- componentId: e.componentId,
2236
- token: i,
2237
- embedType: "report",
2238
- activeFilters: e.filters || {},
2239
- apiClient: this.apiClient,
2240
- renderer: o
2241
- });
2242
- } catch (i) {
2243
- throw this.showError(s, (i == null ? void 0 : i.message) || "Failed to load report"), (r = e.onError) == null || r.call(e, (i == null ? void 0 : i.message) || "Failed to load report"), i;
2244
- }
2245
- }
2246
- // ── Private helpers ─────────────────────────────────────────────────────────
2247
- createRoot(e, t) {
2248
- e.innerHTML = "";
2249
- const s = document.createElement("div");
2250
- return s.className = `ds-embed-root ds-theme-${t}`, s.style.width = "100%", s.style.height = "100%", e.appendChild(s), s;
2251
- }
2252
- showLoading(e) {
2253
- e.innerHTML = `
2254
- <div class="ds-embed-loading">
2255
- <div class="ds-embed-spinner"></div>
2256
- <div class="ds-embed-loading-text">Loading…</div>
2257
- </div>
2258
- `;
2259
- }
2260
- showError(e, t) {
2261
- e.innerHTML = `
2262
- <div class="ds-embed-error">
2263
- <div class="ds-embed-error-icon">⚠</div>
2264
- <div>${t}</div>
2265
- <div class="ds-embed-error-message">Check your SDK configuration.</div>
2266
- </div>
2267
- `;
2268
- }
2269
- }
2270
- export {
2271
- he as DeepspotSDK
2272
- };