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