deepspotscreen-sdk 0.1.1 → 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,2202 +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, 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 || (a.length === 1 ? a[0].column : "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
- };