@giridharan_r/sdk 1.0.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,1392 @@
1
+ class D {
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, d, o;
8
+ const t = [
9
+ e.dashboardId,
10
+ e.embedType,
11
+ (i = e.embedLevel) != null ? i : "",
12
+ (a = e.componentId) != null ? a : "",
13
+ (d = e.userId) != null ? d : ""
14
+ ].join(":"), r = this.tokenCache.get(t);
15
+ if (r && r.expiresAt > Date.now() + 6e4) return r.token;
16
+ const s = await this.post(
17
+ "/dashboard-builder/embed-token",
18
+ {
19
+ dashboardId: e.dashboardId,
20
+ embedType: e.embedType,
21
+ embedLevel: (o = e.embedLevel) != null ? o : "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: s.token,
31
+ expiresAt: new Date(s.expiresAt).getTime()
32
+ }), s.token;
33
+ }
34
+ // ── Dashboard Render (lazy — loads one tab at a time) ──────────────────────
35
+ async getDashboardRender(e, t, r = {}) {
36
+ var i;
37
+ const s = new URLSearchParams({
38
+ embedType: "dashboard",
39
+ embedLevel: (i = r.embedLevel) != null ? i : "dashboard"
40
+ });
41
+ return r.pageId && s.set("pageId", r.pageId), r.tabId && s.set("tabId", r.tabId), r.filters && Object.entries(r.filters).forEach(([a, d]) => {
42
+ d != null && d !== "" && s.set(`filter[${a}]`, Array.isArray(d) ? d.join(",") : String(d));
43
+ }), this.get(
44
+ `/dashboard-builder/embed/${e}/render?${s}`,
45
+ { "x-embed-token": t }
46
+ );
47
+ }
48
+ // ── Single Report Render ────────────────────────────────────────────────────
49
+ async getReportRender(e, t, r, s = {}) {
50
+ const i = new URLSearchParams({
51
+ embedType: "report",
52
+ embedLevel: "report",
53
+ componentId: t
54
+ });
55
+ return s.filters && Object.entries(s.filters).forEach(([a, d]) => {
56
+ d != null && d !== "" && i.set(`filter[${a}]`, Array.isArray(d) ? d.join(",") : String(d));
57
+ }), this.get(
58
+ `/dashboard-builder/embed/${e}/render?${i}`,
59
+ { "x-embed-token": r }
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
+ clearTokenCache() {
79
+ this.tokenCache.clear();
80
+ }
81
+ // ── Private helpers ─────────────────────────────────────────────────────────
82
+ async get(e, t = {}) {
83
+ const r = await fetch(`${this.baseUrl}${e}`, {
84
+ method: "GET",
85
+ headers: { "Content-Type": "application/json", ...t }
86
+ });
87
+ return this.handleResponse(r);
88
+ }
89
+ async post(e, t, r = {}) {
90
+ const s = await fetch(`${this.baseUrl}${e}`, {
91
+ method: "POST",
92
+ headers: { "Content-Type": "application/json", ...r },
93
+ body: JSON.stringify(t)
94
+ });
95
+ return this.handleResponse(s);
96
+ }
97
+ async delete(e, t = {}) {
98
+ const r = await fetch(`${this.baseUrl}${e}`, {
99
+ method: "DELETE",
100
+ headers: { "Content-Type": "application/json", ...t }
101
+ });
102
+ if (!r.ok) throw new Error(`Deepspot SDK: DELETE ${e} failed (${r.status})`);
103
+ }
104
+ async handleResponse(e) {
105
+ if (!e.ok) {
106
+ let t = `HTTP ${e.status}`;
107
+ try {
108
+ const r = await e.json();
109
+ t = (r == null ? void 0 : r.message) || (r == null ? void 0 : r.error) || t;
110
+ } catch (r) {
111
+ }
112
+ throw new Error(`Deepspot SDK: ${t}`);
113
+ }
114
+ return e.json();
115
+ }
116
+ }
117
+ class k {
118
+ constructor(e) {
119
+ var t, r;
120
+ this.destroyed = !1, this.refreshTimer = null, this.opts = e, this.filters = { ...e.activeFilters }, this.activePageId = (t = e.activePageId) != null ? t : "", this.activeTabId = (r = e.activeTabId) != null ? r : "";
121
+ }
122
+ /** Returns a copy of the currently applied filters */
123
+ getActiveFilters() {
124
+ return { ...this.filters };
125
+ }
126
+ /** Apply a single filter and re-fetch data */
127
+ setFilter(e, t) {
128
+ this.filters[e] = t, this.scheduleRefresh();
129
+ }
130
+ /** Apply multiple filters at once and re-fetch */
131
+ setFilters(e) {
132
+ Object.assign(this.filters, e), this.scheduleRefresh();
133
+ }
134
+ /** Navigate to a different page (dashboard embed only) */
135
+ goToPage(e) {
136
+ this.opts.embedType === "dashboard" && (this.activePageId = e, this.opts.renderer.goToPage(e));
137
+ }
138
+ /** Navigate to a specific tab on a specific page */
139
+ goToTab(e, t) {
140
+ this.opts.embedType === "dashboard" && (this.activePageId = e, this.activeTabId = t, this.opts.renderer.goToTab(e, t));
141
+ }
142
+ /** Force re-fetch all data from backend */
143
+ async refresh() {
144
+ if (!this.destroyed)
145
+ try {
146
+ const e = await this.fetchData();
147
+ this.opts.renderer.update(e);
148
+ } catch (e) {
149
+ console.error("Deepspot SDK: refresh failed", e);
150
+ }
151
+ }
152
+ /** Export PDF (dashboard embed only) */
153
+ exportPDF() {
154
+ this.opts.embedType === "dashboard" && this.opts.renderer.exportPDF();
155
+ }
156
+ /** Remove the embed and clean up */
157
+ destroy() {
158
+ this.destroyed = !0, this.refreshTimer && clearTimeout(this.refreshTimer), this.opts.renderer.destroy(), this.opts.apiClient.clearTokenCache();
159
+ }
160
+ // ── Private ─────────────────────────────────────────────────────────────────
161
+ /** Debounce rapid filter changes into a single fetch */
162
+ scheduleRefresh() {
163
+ this.refreshTimer && clearTimeout(this.refreshTimer), this.refreshTimer = setTimeout(() => this.refresh(), 300);
164
+ }
165
+ async fetchData() {
166
+ const { apiClient: e, dashboardId: t, componentId: r, token: s, embedType: i, embedLevel: a } = this.opts;
167
+ return i === "report" && r ? e.getReportRender(t, r, s, {
168
+ filters: this.filters
169
+ }) : e.getDashboardRender(t, s, {
170
+ embedLevel: a,
171
+ pageId: this.activePageId || void 0,
172
+ tabId: this.activeTabId || void 0,
173
+ filters: this.filters
174
+ });
175
+ }
176
+ }
177
+ const I = 24, y = 10, f = 8;
178
+ class $ {
179
+ constructor(e) {
180
+ this.containerWidth = e;
181
+ }
182
+ /** Pixel width of one column unit */
183
+ get colWidth() {
184
+ return (this.containerWidth - f * (I - 1)) / I;
185
+ }
186
+ /** Convert grid position {x,y,w,h} → CSS absolute pixel values */
187
+ toPx(e) {
188
+ const t = this.colWidth;
189
+ return {
190
+ left: e.x * (t + f),
191
+ top: e.y * (y + f),
192
+ width: e.w * t + (e.w - 1) * f,
193
+ height: e.h * y + (e.h - 1) * f
194
+ };
195
+ }
196
+ /** Total height of the grid needed to fit all components */
197
+ static totalHeight(e) {
198
+ return e.length ? Math.max(...e.map((r) => r.y + r.h)) * (y + f) : 400;
199
+ }
200
+ /** Apply absolute positioning to a DOM element */
201
+ applyStyles(e, t) {
202
+ const r = this.toPx(t);
203
+ e.style.position = "absolute", e.style.left = `${r.left}px`, e.style.top = `${r.top}px`, e.style.width = `${r.width}px`, e.style.height = `${r.height}px`;
204
+ }
205
+ }
206
+ class C {
207
+ constructor(e) {
208
+ this.chart = null, this.container = e;
209
+ }
210
+ render(e, t, r) {
211
+ if (!t || t.length === 0) {
212
+ this.renderEmpty(e.title);
213
+ return;
214
+ }
215
+ const s = this.buildOptions(e, t, r);
216
+ if (!s) {
217
+ this.renderEmpty(e.title);
218
+ return;
219
+ }
220
+ this.container.innerHTML = `
221
+ <div class="ds-chart-card">
222
+ <div class="ds-chart-title">${e.title || ""}</div>
223
+ <div class="ds-chart-body" id="ds-chart-body-${e.id}"></div>
224
+ </div>
225
+ `;
226
+ const i = this.container.querySelector(`#ds-chart-body-${e.id}`);
227
+ if (i)
228
+ try {
229
+ this.chart && this.chart.destroy(), this.chart = new ApexCharts(i, s), this.chart.render();
230
+ } catch (a) {
231
+ i.innerHTML = '<div class="ds-chart-empty">Chart render error</div>';
232
+ }
233
+ }
234
+ update(e, t, r) {
235
+ if (!this.chart || !t || t.length === 0) {
236
+ this.render(e, t, r);
237
+ return;
238
+ }
239
+ const { series: s, categories: i } = this.extractSeriesAndCategories(e, t);
240
+ try {
241
+ this.chart.updateOptions({
242
+ series: s,
243
+ xaxis: { categories: i },
244
+ theme: { mode: r }
245
+ });
246
+ } catch (a) {
247
+ this.render(e, t, r);
248
+ }
249
+ }
250
+ destroy() {
251
+ var e;
252
+ try {
253
+ (e = this.chart) == null || e.destroy();
254
+ } catch (t) {
255
+ }
256
+ this.chart = null;
257
+ }
258
+ // ── Private ─────────────────────────────────────────────────────────────────
259
+ buildOptions(e, t, r) {
260
+ const s = e.type;
261
+ return s === "pie" || s === "donut" ? this.buildPieOptions(e, t, r) : s === "bar" || s === "line" || s === "area" ? this.buildCartesianOptions(e, t, r) : null;
262
+ }
263
+ buildCartesianOptions(e, t, r) {
264
+ var n;
265
+ const { series: s, categories: i, xAxisLabel: a, yAxisLabel: d } = this.extractSeriesAndCategories(e, t), o = e.properties || {}, c = o.colors ? Object.values(o.colors) : ["#6366f1", "#8b5cf6", "#ec4899", "#f59e0b", "#10b981", "#3b82f6"], l = r === "dark";
266
+ return {
267
+ chart: {
268
+ type: e.type === "area" ? "area" : e.type === "line" ? "line" : "bar",
269
+ toolbar: { show: !1 },
270
+ background: "transparent",
271
+ animations: { enabled: !0, speed: 400 },
272
+ fontFamily: "inherit"
273
+ },
274
+ theme: { mode: r },
275
+ series: s,
276
+ xaxis: {
277
+ categories: i,
278
+ title: { text: a },
279
+ labels: {
280
+ rotate: i.length > 10 ? -45 : 0,
281
+ style: { colors: l ? "#94a3b8" : "#6b7280", fontSize: "11px" }
282
+ },
283
+ axisBorder: { color: l ? "#334155" : "#e5e7eb" },
284
+ axisTicks: { color: l ? "#334155" : "#e5e7eb" }
285
+ },
286
+ yaxis: {
287
+ title: { text: d },
288
+ labels: { style: { colors: l ? "#94a3b8" : "#6b7280", fontSize: "11px" } }
289
+ },
290
+ colors: c,
291
+ legend: {
292
+ show: s.length > 1 || ((n = o.showLegend) != null ? n : !1),
293
+ position: "bottom",
294
+ labels: { colors: l ? "#94a3b8" : "#6b7280" }
295
+ },
296
+ grid: {
297
+ borderColor: l ? "#1e293b" : "#f3f4f6",
298
+ strokeDashArray: 4
299
+ },
300
+ tooltip: { theme: r },
301
+ stroke: {
302
+ curve: "smooth",
303
+ width: e.type === "bar" ? 0 : 2
304
+ },
305
+ fill: e.type === "area" ? { type: "gradient", gradient: { shadeIntensity: 0.5, opacityFrom: 0.4, opacityTo: 0 } } : {},
306
+ dataLabels: { enabled: !1 },
307
+ plotOptions: {
308
+ bar: {
309
+ borderRadius: 4,
310
+ distributed: s.length === 1
311
+ }
312
+ }
313
+ };
314
+ }
315
+ buildPieOptions(e, t, r) {
316
+ const s = e.properties || {}, i = t.length > 0 ? Object.keys(t[0]) : [], a = i[0] || "label", d = i[1] || "value", o = t.map((h) => {
317
+ var b;
318
+ return String((b = h[a]) != null ? b : "");
319
+ }), c = t.map((h) => Number(h[d]) || 0), l = r === "dark", n = s.colors ? Object.values(s.colors) : ["#6366f1", "#8b5cf6", "#ec4899", "#f59e0b", "#10b981", "#3b82f6"];
320
+ return {
321
+ chart: {
322
+ type: e.type,
323
+ toolbar: { show: !1 },
324
+ background: "transparent",
325
+ fontFamily: "inherit"
326
+ },
327
+ theme: { mode: r },
328
+ series: c,
329
+ labels: o,
330
+ colors: n,
331
+ legend: {
332
+ position: "bottom",
333
+ labels: { colors: l ? "#94a3b8" : "#6b7280" }
334
+ },
335
+ tooltip: { theme: r },
336
+ dataLabels: { enabled: o.length <= 8 },
337
+ plotOptions: {
338
+ pie: {
339
+ donut: { size: e.type === "donut" ? "65%" : "0%" }
340
+ }
341
+ }
342
+ };
343
+ }
344
+ extractSeriesAndCategories(e, t) {
345
+ const r = e.properties || {}, s = t.length > 0 ? Object.keys(t[0]) : [], i = r.xAxis || s[0] || "x";
346
+ let a = [];
347
+ if (r.selectedYAxisColumn)
348
+ try {
349
+ const n = typeof r.selectedYAxisColumn == "string" ? JSON.parse(r.selectedYAxisColumn) : r.selectedYAxisColumn;
350
+ Array.isArray(n) && (a = n.map(
351
+ (h) => typeof h == "string" ? { column: h } : h
352
+ ));
353
+ } catch (n) {
354
+ }
355
+ if (!a.length && r.yAxis) {
356
+ const n = r.yAxis;
357
+ a = (Array.isArray(n) ? n : [n]).map((b) => ({ column: String(b) }));
358
+ }
359
+ !a.length && s.length > 1 && (a = s.slice(1).map((n) => ({ column: n })));
360
+ const d = t.map((n) => {
361
+ var h;
362
+ return String((h = n[i]) != null ? h : "");
363
+ }), o = a.map((n) => ({
364
+ name: n.label || n.column.replace(/_/g, " "),
365
+ data: t.map((h) => {
366
+ const b = h[n.column];
367
+ return b != null ? Number(b) : null;
368
+ })
369
+ })), c = r.xAxisLabel || i.replace(/_/g, " "), l = r.yAxisLabel || (o.length === 1 ? o[0].name : "Values");
370
+ return { series: o, categories: d, xAxisLabel: c, yAxisLabel: l };
371
+ }
372
+ renderEmpty(e) {
373
+ this.container.innerHTML = `
374
+ <div class="ds-chart-card">
375
+ ${e ? `<div class="ds-chart-title">${e}</div>` : ""}
376
+ <div class="ds-chart-empty">No data available</div>
377
+ </div>
378
+ `;
379
+ }
380
+ }
381
+ class L {
382
+ render(e, t, r) {
383
+ var c;
384
+ if (!r || r.length === 0) {
385
+ e.innerHTML = `
386
+ <div class="ds-table-card">
387
+ <div class="ds-table-title">${t.title || ""}</div>
388
+ <div class="ds-chart-empty">No data available</div>
389
+ </div>
390
+ `;
391
+ return;
392
+ }
393
+ const s = Object.keys(r[0]), i = ((c = t.properties) == null ? void 0 : c.columns) || [], a = i.length ? i.filter((l) => s.includes(l)) : s, d = a.map((l) => `<th>${this.formatHeader(l)}</th>`).join(""), o = r.map((l) => `<tr>${a.map((h) => {
394
+ const b = l[h];
395
+ return `<td title="${this.escape(String(b != null ? b : ""))}">${this.escape(this.formatValue(b))}</td>`;
396
+ }).join("")}</tr>`).join("");
397
+ e.innerHTML = `
398
+ <div class="ds-table-card">
399
+ <div class="ds-table-title">${t.title || ""}</div>
400
+ <div class="ds-table-scroll">
401
+ <table class="ds-table">
402
+ <thead><tr>${d}</tr></thead>
403
+ <tbody>${o}</tbody>
404
+ </table>
405
+ </div>
406
+ </div>
407
+ `;
408
+ }
409
+ formatHeader(e) {
410
+ return e.replace(/_/g, " ").replace(/\b\w/g, (t) => t.toUpperCase());
411
+ }
412
+ formatValue(e) {
413
+ return e == null ? "—" : typeof e == "number" ? e % 1 === 0 ? e.toLocaleString() : e.toFixed(2) : String(e);
414
+ }
415
+ escape(e) {
416
+ return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
417
+ }
418
+ }
419
+ class F {
420
+ render(e, t, r) {
421
+ const s = t.properties || {};
422
+ let i = null;
423
+ if (r && r.length > 0) {
424
+ const l = r[0], n = s.metric || Object.keys(l)[0];
425
+ i = l[n];
426
+ }
427
+ const a = this.formatValue(i, s), d = s.backgroundColor || "#ffffff", o = s.textColor || "#111827", c = t.title || "";
428
+ e.innerHTML = `
429
+ <div class="ds-card" style="background:${d}; border-color:${d};">
430
+ <div class="ds-card-label" style="color:${o}; opacity:0.7;">${c}</div>
431
+ <div class="ds-card-value" style="color:${o};">${a}</div>
432
+ </div>
433
+ `;
434
+ }
435
+ formatValue(e, t) {
436
+ if (e == null) return "—";
437
+ const r = Number(e);
438
+ if (isNaN(r)) return String(e);
439
+ const s = (t == null ? void 0 : t.prefix) || (t == null ? void 0 : t.currencySymbol) || "", i = (t == null ? void 0 : t.suffix) || "";
440
+ let a;
441
+ return Math.abs(r) >= 1e6 ? a = (r / 1e6).toFixed(1) + "M" : Math.abs(r) >= 1e3 ? a = (r / 1e3).toFixed(1) + "K" : a = r % 1 === 0 ? r.toLocaleString() : r.toFixed(2), `${s}${a}${i}`;
442
+ }
443
+ }
444
+ class E {
445
+ constructor(e, t) {
446
+ this.currentValues = {}, this.container = e, this.onFilterChange = t;
447
+ }
448
+ render(e, t = {}) {
449
+ if (!e || e.length === 0) {
450
+ this.container.style.display = "none";
451
+ return;
452
+ }
453
+ this.container.style.display = "", this.currentValues = { ...t }, this.container.innerHTML = `<div class="ds-filter-bar">${e.map((r) => this.renderFilter(r, t[r.applyToField || r.filterId])).join("")}</div>`, this.attachListeners(e);
454
+ }
455
+ /** Update the stored values without re-rendering (for programmatic setFilter) */
456
+ updateValues(e) {
457
+ Object.assign(this.currentValues, e), Object.entries(e).forEach(([t, r]) => {
458
+ const s = this.container.querySelector(`[data-filter-id="${t}"]`);
459
+ s && r !== void 0 && (s.value = String(r));
460
+ });
461
+ }
462
+ // ── Private ─────────────────────────────────────────────────────────────────
463
+ renderFilter(e, t) {
464
+ const r = `ds-filter-${e.filterId}`, s = `<label class="ds-filter-label" for="${r}">${e.label}</label>`;
465
+ switch (e.type) {
466
+ case "dropdown":
467
+ return `
468
+ <div class="ds-filter-item">
469
+ ${s}
470
+ <select class="ds-filter-select" id="${r}" data-filter-id="${e.filterId}">
471
+ <option value="">All</option>
472
+ ${(e.options || []).map((d) => `
473
+ <option value="${this.escAttr(d)}" ${t === d ? "selected" : ""}>
474
+ ${this.escText(d)}
475
+ </option>
476
+ `).join("")}
477
+ </select>
478
+ </div>`;
479
+ case "multi-select":
480
+ return `
481
+ <div class="ds-filter-item">
482
+ ${s}
483
+ <select class="ds-filter-select" id="${r}" data-filter-id="${e.filterId}" multiple size="1">
484
+ ${(e.options || []).map((d) => `
485
+ <option value="${this.escAttr(d)}">${this.escText(d)}</option>
486
+ `).join("")}
487
+ </select>
488
+ </div>`;
489
+ case "date-range":
490
+ const i = (t == null ? void 0 : t.from) || "", a = (t == null ? void 0 : t.to) || "";
491
+ return `
492
+ <div class="ds-filter-item">
493
+ ${s}
494
+ <div class="ds-date-range-inputs">
495
+ <input type="date" class="ds-filter-input"
496
+ data-filter-id="${e.filterId}" data-date-part="from"
497
+ value="${i}" />
498
+ <span>–</span>
499
+ <input type="date" class="ds-filter-input"
500
+ data-filter-id="${e.filterId}" data-date-part="to"
501
+ value="${a}" />
502
+ </div>
503
+ </div>`;
504
+ case "text-input":
505
+ return `
506
+ <div class="ds-filter-item">
507
+ ${s}
508
+ <input type="text" class="ds-filter-input" id="${r}"
509
+ data-filter-id="${e.filterId}"
510
+ value="${this.escAttr(String(t != null ? t : ""))}"
511
+ placeholder="Search..." />
512
+ </div>`;
513
+ case "number-input":
514
+ return `
515
+ <div class="ds-filter-item">
516
+ ${s}
517
+ <input type="number" class="ds-filter-input" id="${r}"
518
+ data-filter-id="${e.filterId}"
519
+ value="${this.escAttr(String(t != null ? t : ""))}" />
520
+ </div>`;
521
+ default:
522
+ return "";
523
+ }
524
+ }
525
+ attachListeners(e) {
526
+ const t = {};
527
+ this.container.querySelectorAll("[data-filter-id]").forEach((r) => {
528
+ const s = r.dataset.filterId, i = r.dataset.datePart, a = e.find((o) => o.filterId === s), d = () => {
529
+ let o;
530
+ if ((a == null ? void 0 : a.type) === "date-range" && i)
531
+ t[s] = t[s] || {}, t[s][i] = r.value, o = { ...t[s] };
532
+ else if ((a == null ? void 0 : a.type) === "multi-select") {
533
+ const c = r;
534
+ o = Array.from(c.selectedOptions).map((l) => l.value).filter(Boolean);
535
+ } else
536
+ o = r.value;
537
+ this.currentValues[s] = o, this.onFilterChange(s, o);
538
+ };
539
+ r.addEventListener(
540
+ (a == null ? void 0 : a.type) === "text-input" ? "input" : "change",
541
+ d
542
+ );
543
+ });
544
+ }
545
+ escAttr(e) {
546
+ return e.replace(/"/g, "&quot;").replace(/'/g, "&#39;");
547
+ }
548
+ escText(e) {
549
+ return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
550
+ }
551
+ }
552
+ class A {
553
+ constructor(e, t) {
554
+ this.activePageId = "", this.activeTabId = "", this.activeFilters = {}, this.chartRenderers = /* @__PURE__ */ new Map(), this.tableRenderer = new L(), this.cardRenderer = new F(), this.isLoadingTab = !1, this.root = e, this.opts = t, this.activeFilters = { ...t.initialFilters };
555
+ }
556
+ // ── Initial full render ───────────────────────────────────────────────────
557
+ render(e) {
558
+ var t, r;
559
+ this.renderData = e, this.activePageId = e.activePage, this.activeTabId = e.activeTab, this.buildShell(), this.renderNavigation(), this.renderFilterBar(), this.renderGrid(), (r = (t = this.opts).onReady) == null || r.call(t);
560
+ }
561
+ // ── Called after lazy tab fetch completes ─────────────────────────────────
562
+ update(e) {
563
+ this.renderData = e, this.activePageId = e.activePage, this.activeTabId = e.activeTab, this.isLoadingTab = !1, this.syncNavHighlight(), this.updateFilterBar(), this.renderGrid();
564
+ }
565
+ goToPage(e) {
566
+ const t = this.getTabsForPage(e)[0];
567
+ t && this.triggerTabSwitch(e, t.id);
568
+ }
569
+ goToTab(e, t) {
570
+ this.triggerTabSwitch(e, t);
571
+ }
572
+ destroy() {
573
+ this.chartRenderers.forEach((e) => e.destroy()), this.chartRenderers.clear(), this.root.innerHTML = "";
574
+ }
575
+ exportPDF() {
576
+ const e = this.root.querySelector("#ds-canvas");
577
+ e && import("./html2canvas.esm-CzwMv54K.js").then(
578
+ ({ default: t }) => t(e, { scale: 2, useCORS: !0 }).then(
579
+ (r) => import("./jspdf.es.min-DIdZC3N_.js").then((s) => s.j).then(({ jsPDF: s }) => {
580
+ const i = new s({ orientation: "landscape", unit: "px" });
581
+ i.addImage(r.toDataURL("image/png"), "PNG", 0, 0, i.internal.pageSize.width, 0), i.save(`${this.renderData.dashboard.name || "dashboard"}.pdf`);
582
+ })
583
+ )
584
+ );
585
+ }
586
+ exportCSV() {
587
+ this.renderData.components.forEach((e) => {
588
+ const t = this.renderData.data[e.id];
589
+ if (!(t != null && t.length) || !["table", "bar", "line", "area"].includes(e.type)) return;
590
+ const r = Object.keys(t[0]), s = [r.join(","), ...t.map(
591
+ (a) => r.map((d) => {
592
+ var o;
593
+ return JSON.stringify((o = a[d]) != null ? o : "");
594
+ }).join(",")
595
+ )].join(`
596
+ `);
597
+ Object.assign(document.createElement("a"), {
598
+ href: URL.createObjectURL(new Blob([s], { type: "text/csv" })),
599
+ download: `${e.title || e.id}.csv`
600
+ }).click();
601
+ });
602
+ }
603
+ updateFilterValues(e) {
604
+ var t;
605
+ Object.assign(this.activeFilters, e), (t = this.filterRenderer) == null || t.updateValues(e);
606
+ }
607
+ // ── Shell ─────────────────────────────────────────────────────────────────
608
+ buildShell() {
609
+ const e = this.opts.embedLevel, t = this.renderData.dashboard.pages, r = e === "dashboard" && t.length > 1, s = e === "page" || e === "dashboard";
610
+ this.root.innerHTML = `
611
+ ${this.opts.hideExport ? "" : '<div class="ds-toolbar" id="ds-toolbar"></div>'}
612
+ ${r ? '<nav class="ds-page-nav" id="ds-page-nav"></nav>' : ""}
613
+ ${s ? '<nav class="ds-page-nav" id="ds-tab-nav"></nav>' : ""}
614
+ <div id="ds-filter-container"></div>
615
+ <div class="ds-canvas" id="ds-canvas">
616
+ <div class="ds-grid" id="ds-grid"></div>
617
+ </div>
618
+ <div id="ds-tab-loading" style="display:none;position:absolute;inset:0;
619
+ background:rgba(255,255,255,0.5);align-items:center;justify-content:center;">
620
+ <div class="ds-embed-spinner"></div>
621
+ </div>
622
+ `, this.root.style.position = "relative", this.opts.hideExport || this.buildExportToolbar();
623
+ const i = this.root.querySelector("#ds-filter-container");
624
+ this.filterRenderer = new E(i, (a, d) => {
625
+ var o, c;
626
+ this.activeFilters[a] = d, (c = (o = this.opts).onFilterChange) == null || c.call(o, this.activeFilters), this.opts.onTabSwitch(this.activePageId, this.activeTabId);
627
+ });
628
+ }
629
+ buildExportToolbar() {
630
+ var t, r;
631
+ const e = this.root.querySelector("#ds-toolbar");
632
+ e && (e.innerHTML = `
633
+ <button class="ds-toolbar-btn" id="ds-btn-pdf">⬇ PDF</button>
634
+ <button class="ds-toolbar-btn" id="ds-btn-csv">⬇ CSV</button>
635
+ `, (t = e.querySelector("#ds-btn-pdf")) == null || t.addEventListener("click", () => this.exportPDF()), (r = e.querySelector("#ds-btn-csv")) == null || r.addEventListener("click", () => this.exportCSV()));
636
+ }
637
+ // ── Navigation ────────────────────────────────────────────────────────────
638
+ renderNavigation() {
639
+ this.renderPageNav(), this.renderTabNav(this.activePageId);
640
+ }
641
+ renderPageNav() {
642
+ const e = this.root.querySelector("#ds-page-nav");
643
+ e && (e.innerHTML = this.renderData.dashboard.pages.map((t) => `
644
+ <button class="ds-page-tab ${t.pageId === this.activePageId ? "ds-active" : ""}"
645
+ data-page-id="${t.pageId}">${t.title}</button>
646
+ `).join(""), e.querySelectorAll(".ds-page-tab").forEach((t) => {
647
+ t.addEventListener("click", () => {
648
+ var s, i;
649
+ const r = t.dataset.pageId;
650
+ r === this.activePageId && !this.isLoadingTab || this.triggerTabSwitch(r, (i = (s = this.getTabsForPage(r)[0]) == null ? void 0 : s.id) != null ? i : "");
651
+ });
652
+ }));
653
+ }
654
+ renderTabNav(e) {
655
+ const t = this.root.querySelector("#ds-tab-nav");
656
+ t && this.buildTabButtons(t, e);
657
+ }
658
+ buildTabButtons(e, t) {
659
+ const r = this.getTabsForPage(t);
660
+ if (r.length <= 1) {
661
+ e.style.display = "none";
662
+ return;
663
+ }
664
+ e.style.display = "", e.innerHTML = r.map((s) => `
665
+ <button class="ds-page-tab ${s.id === this.activeTabId ? "ds-active" : ""}"
666
+ data-tab-id="${s.id}" data-page-id="${t}">${s.title}</button>
667
+ `).join(""), e.querySelectorAll(".ds-page-tab").forEach((s) => {
668
+ s.addEventListener("click", () => {
669
+ const i = s.dataset.tabId, a = s.dataset.pageId;
670
+ i === this.activeTabId && a === this.activePageId && !this.isLoadingTab || this.triggerTabSwitch(a, i);
671
+ });
672
+ });
673
+ }
674
+ /** Optimistically highlight new tab + show spinner, then fire callback */
675
+ triggerTabSwitch(e, t) {
676
+ this.isLoadingTab || !e || !t || (this.isLoadingTab = !0, this.activePageId = e, this.activeTabId = t, this.syncNavHighlight(), this.showTabSpinner(), this.opts.onTabSwitch(e, t));
677
+ }
678
+ syncNavHighlight() {
679
+ this.root.querySelectorAll("#ds-page-nav .ds-page-tab").forEach((t) => {
680
+ t.classList.toggle("ds-active", t.dataset.pageId === this.activePageId);
681
+ });
682
+ const e = this.root.querySelector("#ds-tab-nav");
683
+ e && this.buildTabButtons(e, this.activePageId);
684
+ }
685
+ // ── Filter bar ────────────────────────────────────────────────────────────
686
+ renderFilterBar() {
687
+ this.opts.hideFilters || this.filterRenderer.render(this.renderData.filters, this.activeFilters);
688
+ }
689
+ updateFilterBar() {
690
+ this.opts.hideFilters || this.filterRenderer.render(this.renderData.filters, this.activeFilters);
691
+ }
692
+ // ── Grid ──────────────────────────────────────────────────────────────────
693
+ renderGrid() {
694
+ this.hideTabSpinner();
695
+ const e = this.root.querySelector("#ds-grid");
696
+ if (!e) return;
697
+ this.chartRenderers.forEach((i) => i.destroy()), this.chartRenderers.clear();
698
+ const t = this.renderData.components;
699
+ if (!t.length) {
700
+ e.style.height = "200px", e.innerHTML = '<div class="ds-chart-empty" style="padding-top:80px;">No components on this tab.</div>';
701
+ return;
702
+ }
703
+ const r = e.clientWidth || 1248, s = new $(r);
704
+ e.style.height = `${$.totalHeight(t.map((i) => i.position))}px`, e.innerHTML = "", t.forEach((i) => {
705
+ const a = document.createElement("div");
706
+ a.className = "ds-component-wrapper", a.dataset.componentId = i.id, s.applyStyles(a, i.position), e.appendChild(a), this.renderComponent(a, i);
707
+ });
708
+ }
709
+ renderComponent(e, t) {
710
+ var i, a;
711
+ const r = this.renderData.data[t.id] || [], s = this.opts.theme;
712
+ switch (t.type) {
713
+ case "bar":
714
+ case "line":
715
+ case "pie":
716
+ case "donut":
717
+ case "area": {
718
+ const d = new C(e);
719
+ d.render(t, r, s), this.chartRenderers.set(t.id, d);
720
+ break;
721
+ }
722
+ case "table":
723
+ this.tableRenderer.render(e, t, r);
724
+ break;
725
+ case "number-card":
726
+ this.cardRenderer.render(e, t, r);
727
+ break;
728
+ case "text": {
729
+ const d = Object.entries(((i = t.properties) == null ? void 0 : i.style) || {}).map(([o, c]) => `${o}:${c}`).join(";");
730
+ e.innerHTML = `<div style="${d};padding:8px;">${((a = t.properties) == null ? void 0 : a.content) || ""}</div>`;
731
+ break;
732
+ }
733
+ }
734
+ }
735
+ // ── Spinner ───────────────────────────────────────────────────────────────
736
+ showTabSpinner() {
737
+ const e = this.root.querySelector("#ds-tab-loading"), t = this.root.querySelector("#ds-grid");
738
+ e && (e.style.display = "flex"), t && (t.style.opacity = "0.35");
739
+ }
740
+ hideTabSpinner() {
741
+ const e = this.root.querySelector("#ds-tab-loading"), t = this.root.querySelector("#ds-grid");
742
+ e && (e.style.display = "none"), t && (t.style.opacity = "1");
743
+ }
744
+ // ── Helpers ───────────────────────────────────────────────────────────────
745
+ getTabsForPage(e) {
746
+ var t, r;
747
+ return (r = (t = this.renderData.dashboard.pages.find((s) => s.pageId === e)) == null ? void 0 : t.tabs) != null ? r : [];
748
+ }
749
+ }
750
+ class P {
751
+ constructor(e, t) {
752
+ this.chartRenderer = null, this.tableRenderer = new L(), this.cardRenderer = new F(), this.root = e, this.theme = t;
753
+ }
754
+ render(e, t) {
755
+ const r = e.components[0];
756
+ if (!r) {
757
+ this.root.innerHTML = `<div class="ds-embed-error">
758
+ <div class="ds-embed-error-icon">⚠</div>
759
+ <div>Component not found.</div>
760
+ </div>`;
761
+ return;
762
+ }
763
+ const s = e.data[r.id] || [];
764
+ this.renderComponent(r, s), t == null || t();
765
+ }
766
+ update(e) {
767
+ const t = e.components[0];
768
+ if (!t) return;
769
+ const r = e.data[t.id] || [];
770
+ switch (t.type) {
771
+ case "bar":
772
+ case "line":
773
+ case "pie":
774
+ case "donut":
775
+ case "area":
776
+ this.chartRenderer ? this.chartRenderer.update(t, r, this.theme) : this.renderComponent(t, r);
777
+ break;
778
+ case "table":
779
+ this.tableRenderer.render(this.root, t, r);
780
+ break;
781
+ case "number-card":
782
+ this.cardRenderer.render(this.root, t, r);
783
+ break;
784
+ }
785
+ }
786
+ destroy() {
787
+ var e;
788
+ (e = this.chartRenderer) == null || e.destroy(), this.chartRenderer = null, this.root.innerHTML = "";
789
+ }
790
+ renderComponent(e, t) {
791
+ var r;
792
+ switch (e.type) {
793
+ case "bar":
794
+ case "line":
795
+ case "pie":
796
+ case "donut":
797
+ case "area":
798
+ (r = this.chartRenderer) == null || r.destroy(), this.chartRenderer = new C(this.root), this.chartRenderer.render(e, t, this.theme);
799
+ break;
800
+ case "table":
801
+ this.tableRenderer.render(this.root, e, t);
802
+ break;
803
+ case "number-card":
804
+ this.cardRenderer.render(this.root, e, t);
805
+ break;
806
+ default:
807
+ this.root.innerHTML = `<div class="ds-chart-empty">Unsupported component type: ${e.type}</div>`;
808
+ }
809
+ }
810
+ }
811
+ let S = !1;
812
+ function j() {
813
+ if (!(S || typeof document == "undefined")) {
814
+ S = !0;
815
+ try {
816
+ const p = `/* ─────────────────────────────────────────────────────────────────────────────
817
+ Deepspot SDK — Base Embed Styles
818
+ Injected once into the host page's <head> by the SDK.
819
+ All selectors are scoped under .ds-embed-* to avoid leaking into host styles.
820
+ ───────────────────────────────────────────────────────────────────────────── */
821
+
822
+ /* ── Reset inside SDK containers ──────────────────────────────────────────── */
823
+ .ds-embed-root *,
824
+ .ds-embed-root *::before,
825
+ .ds-embed-root *::after {
826
+ box-sizing: border-box;
827
+ margin: 0;
828
+ padding: 0;
829
+ }
830
+
831
+ /* ── Root container ────────────────────────────────────────────────────────── */
832
+ .ds-embed-root {
833
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
834
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
835
+ font-size: 14px;
836
+ line-height: 1.5;
837
+ width: 100%;
838
+ height: 100%;
839
+ overflow: auto;
840
+ position: relative;
841
+ }
842
+
843
+ .ds-embed-root.ds-theme-light {
844
+ background: #ffffff;
845
+ color: #111827;
846
+ }
847
+
848
+ .ds-embed-root.ds-theme-dark {
849
+ background: #0f172a;
850
+ color: #f1f5f9;
851
+ }
852
+
853
+ /* ── Loading state ─────────────────────────────────────────────────────────── */
854
+ .ds-embed-loading {
855
+ display: flex;
856
+ flex-direction: column;
857
+ align-items: center;
858
+ justify-content: center;
859
+ height: 100%;
860
+ min-height: 200px;
861
+ gap: 12px;
862
+ color: #6b7280;
863
+ }
864
+
865
+ .ds-embed-spinner {
866
+ width: 36px;
867
+ height: 36px;
868
+ border: 3px solid #e5e7eb;
869
+ border-top-color: #6366f1;
870
+ border-radius: 50%;
871
+ animation: ds-spin 0.8s linear infinite;
872
+ }
873
+
874
+ @keyframes ds-spin {
875
+ to { transform: rotate(360deg); }
876
+ }
877
+
878
+ .ds-embed-loading-text {
879
+ font-size: 13px;
880
+ }
881
+
882
+ /* ── Error state ───────────────────────────────────────────────────────────── */
883
+ .ds-embed-error {
884
+ display: flex;
885
+ flex-direction: column;
886
+ align-items: center;
887
+ justify-content: center;
888
+ height: 100%;
889
+ min-height: 200px;
890
+ gap: 8px;
891
+ padding: 24px;
892
+ text-align: center;
893
+ color: #ef4444;
894
+ }
895
+
896
+ .ds-embed-error-icon {
897
+ font-size: 32px;
898
+ }
899
+
900
+ .ds-embed-error-message {
901
+ font-size: 13px;
902
+ color: #6b7280;
903
+ }
904
+
905
+ /* ── Filter bar ────────────────────────────────────────────────────────────── */
906
+ .ds-filter-bar {
907
+ display: flex;
908
+ flex-wrap: wrap;
909
+ align-items: center;
910
+ gap: 10px;
911
+ padding: 10px 16px;
912
+ border-bottom: 1px solid #e5e7eb;
913
+ background: inherit;
914
+ }
915
+
916
+ .ds-theme-dark .ds-filter-bar {
917
+ border-bottom-color: #1e293b;
918
+ }
919
+
920
+ .ds-filter-item {
921
+ display: flex;
922
+ flex-direction: column;
923
+ gap: 4px;
924
+ }
925
+
926
+ .ds-filter-label {
927
+ font-size: 11px;
928
+ font-weight: 600;
929
+ text-transform: uppercase;
930
+ letter-spacing: 0.05em;
931
+ color: #6b7280;
932
+ }
933
+
934
+ .ds-filter-select,
935
+ .ds-filter-input {
936
+ height: 32px;
937
+ padding: 0 10px;
938
+ border: 1px solid #d1d5db;
939
+ border-radius: 6px;
940
+ font-size: 13px;
941
+ background: #ffffff;
942
+ color: #111827;
943
+ cursor: pointer;
944
+ min-width: 130px;
945
+ }
946
+
947
+ .ds-theme-dark .ds-filter-select,
948
+ .ds-theme-dark .ds-filter-input {
949
+ background: #1e293b;
950
+ border-color: #334155;
951
+ color: #f1f5f9;
952
+ }
953
+
954
+ .ds-filter-select:focus,
955
+ .ds-filter-input:focus {
956
+ outline: none;
957
+ border-color: #6366f1;
958
+ box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
959
+ }
960
+
961
+ .ds-date-range-inputs {
962
+ display: flex;
963
+ gap: 6px;
964
+ align-items: center;
965
+ }
966
+
967
+ .ds-date-range-inputs input[type="date"] {
968
+ height: 32px;
969
+ padding: 0 8px;
970
+ border: 1px solid #d1d5db;
971
+ border-radius: 6px;
972
+ font-size: 13px;
973
+ background: #ffffff;
974
+ color: #111827;
975
+ }
976
+
977
+ .ds-theme-dark .ds-date-range-inputs input[type="date"] {
978
+ background: #1e293b;
979
+ border-color: #334155;
980
+ color: #f1f5f9;
981
+ }
982
+
983
+ /* ── Page / Tab navigation ─────────────────────────────────────────────────── */
984
+ .ds-page-nav {
985
+ display: flex;
986
+ gap: 4px;
987
+ padding: 8px 16px 0;
988
+ border-bottom: 1px solid #e5e7eb;
989
+ overflow-x: auto;
990
+ }
991
+
992
+ .ds-theme-dark .ds-page-nav {
993
+ border-bottom-color: #1e293b;
994
+ }
995
+
996
+ .ds-page-tab {
997
+ padding: 6px 16px;
998
+ font-size: 13px;
999
+ font-weight: 500;
1000
+ border: none;
1001
+ border-bottom: 2px solid transparent;
1002
+ background: transparent;
1003
+ color: #6b7280;
1004
+ cursor: pointer;
1005
+ white-space: nowrap;
1006
+ transition: color 0.15s, border-color 0.15s;
1007
+ }
1008
+
1009
+ .ds-page-tab:hover {
1010
+ color: #111827;
1011
+ }
1012
+
1013
+ .ds-theme-dark .ds-page-tab:hover {
1014
+ color: #f1f5f9;
1015
+ }
1016
+
1017
+ .ds-page-tab.ds-active {
1018
+ color: #6366f1;
1019
+ border-bottom-color: #6366f1;
1020
+ }
1021
+
1022
+ /* ── Grid canvas ───────────────────────────────────────────────────────────── */
1023
+ .ds-canvas {
1024
+ position: relative;
1025
+ width: 100%;
1026
+ padding: 16px;
1027
+ }
1028
+
1029
+ .ds-grid {
1030
+ position: relative;
1031
+ width: 100%;
1032
+ }
1033
+
1034
+ .ds-component-wrapper {
1035
+ position: absolute;
1036
+ overflow: hidden;
1037
+ }
1038
+
1039
+ /* ── Chart component ───────────────────────────────────────────────────────── */
1040
+ .ds-chart-card {
1041
+ width: 100%;
1042
+ height: 100%;
1043
+ background: #ffffff;
1044
+ border: 1px solid #e5e7eb;
1045
+ border-radius: 10px;
1046
+ overflow: hidden;
1047
+ display: flex;
1048
+ flex-direction: column;
1049
+ }
1050
+
1051
+ .ds-theme-dark .ds-chart-card {
1052
+ background: #1e293b;
1053
+ border-color: #334155;
1054
+ }
1055
+
1056
+ .ds-chart-title {
1057
+ padding: 10px 14px 4px;
1058
+ font-size: 13px;
1059
+ font-weight: 600;
1060
+ color: #374151;
1061
+ flex-shrink: 0;
1062
+ }
1063
+
1064
+ .ds-theme-dark .ds-chart-title {
1065
+ color: #e2e8f0;
1066
+ }
1067
+
1068
+ .ds-chart-body {
1069
+ flex: 1;
1070
+ min-height: 0;
1071
+ }
1072
+
1073
+ .ds-chart-empty {
1074
+ display: flex;
1075
+ align-items: center;
1076
+ justify-content: center;
1077
+ height: 100%;
1078
+ color: #9ca3af;
1079
+ font-size: 13px;
1080
+ }
1081
+
1082
+ /* ── Table component ───────────────────────────────────────────────────────── */
1083
+ .ds-table-card {
1084
+ width: 100%;
1085
+ height: 100%;
1086
+ background: #ffffff;
1087
+ border: 1px solid #e5e7eb;
1088
+ border-radius: 10px;
1089
+ overflow: hidden;
1090
+ display: flex;
1091
+ flex-direction: column;
1092
+ }
1093
+
1094
+ .ds-theme-dark .ds-table-card {
1095
+ background: #1e293b;
1096
+ border-color: #334155;
1097
+ }
1098
+
1099
+ .ds-table-title {
1100
+ padding: 10px 14px 4px;
1101
+ font-size: 13px;
1102
+ font-weight: 600;
1103
+ color: #374151;
1104
+ flex-shrink: 0;
1105
+ }
1106
+
1107
+ .ds-theme-dark .ds-table-title {
1108
+ color: #e2e8f0;
1109
+ }
1110
+
1111
+ .ds-table-scroll {
1112
+ flex: 1;
1113
+ overflow: auto;
1114
+ }
1115
+
1116
+ .ds-table {
1117
+ width: 100%;
1118
+ border-collapse: collapse;
1119
+ font-size: 12px;
1120
+ }
1121
+
1122
+ .ds-table th {
1123
+ position: sticky;
1124
+ top: 0;
1125
+ padding: 8px 12px;
1126
+ text-align: left;
1127
+ font-weight: 600;
1128
+ font-size: 11px;
1129
+ text-transform: uppercase;
1130
+ letter-spacing: 0.05em;
1131
+ background: #f9fafb;
1132
+ color: #6b7280;
1133
+ border-bottom: 1px solid #e5e7eb;
1134
+ white-space: nowrap;
1135
+ }
1136
+
1137
+ .ds-theme-dark .ds-table th {
1138
+ background: #0f172a;
1139
+ color: #94a3b8;
1140
+ border-bottom-color: #334155;
1141
+ }
1142
+
1143
+ .ds-table td {
1144
+ padding: 8px 12px;
1145
+ border-bottom: 1px solid #f3f4f6;
1146
+ color: #374151;
1147
+ max-width: 200px;
1148
+ overflow: hidden;
1149
+ text-overflow: ellipsis;
1150
+ white-space: nowrap;
1151
+ }
1152
+
1153
+ .ds-theme-dark .ds-table td {
1154
+ color: #cbd5e1;
1155
+ border-bottom-color: #1e293b;
1156
+ }
1157
+
1158
+ .ds-table tr:hover td {
1159
+ background: #f9fafb;
1160
+ }
1161
+
1162
+ .ds-theme-dark .ds-table tr:hover td {
1163
+ background: #1e293b;
1164
+ }
1165
+
1166
+ /* ── KPI Number Card ───────────────────────────────────────────────────────── */
1167
+ .ds-card {
1168
+ width: 100%;
1169
+ height: 100%;
1170
+ border: 1px solid #e5e7eb;
1171
+ border-radius: 10px;
1172
+ display: flex;
1173
+ flex-direction: column;
1174
+ align-items: center;
1175
+ justify-content: center;
1176
+ padding: 16px;
1177
+ text-align: center;
1178
+ background: #ffffff;
1179
+ }
1180
+
1181
+ .ds-theme-dark .ds-card {
1182
+ background: #1e293b;
1183
+ border-color: #334155;
1184
+ }
1185
+
1186
+ .ds-card-label {
1187
+ font-size: 12px;
1188
+ font-weight: 500;
1189
+ color: #6b7280;
1190
+ margin-bottom: 6px;
1191
+ text-transform: uppercase;
1192
+ letter-spacing: 0.05em;
1193
+ }
1194
+
1195
+ .ds-card-value {
1196
+ font-size: 32px;
1197
+ font-weight: 700;
1198
+ color: #111827;
1199
+ line-height: 1.1;
1200
+ }
1201
+
1202
+ .ds-theme-dark .ds-card-value {
1203
+ color: #f1f5f9;
1204
+ }
1205
+
1206
+ /* ── Export toolbar ────────────────────────────────────────────────────────── */
1207
+ .ds-toolbar {
1208
+ display: flex;
1209
+ justify-content: flex-end;
1210
+ padding: 8px 16px 0;
1211
+ gap: 8px;
1212
+ }
1213
+
1214
+ .ds-toolbar-btn {
1215
+ height: 30px;
1216
+ padding: 0 12px;
1217
+ border: 1px solid #d1d5db;
1218
+ border-radius: 6px;
1219
+ font-size: 12px;
1220
+ font-weight: 500;
1221
+ background: #ffffff;
1222
+ color: #374151;
1223
+ cursor: pointer;
1224
+ display: flex;
1225
+ align-items: center;
1226
+ gap: 5px;
1227
+ transition: background 0.15s, border-color 0.15s;
1228
+ }
1229
+
1230
+ .ds-toolbar-btn:hover {
1231
+ background: #f9fafb;
1232
+ border-color: #9ca3af;
1233
+ }
1234
+
1235
+ .ds-theme-dark .ds-toolbar-btn {
1236
+ background: #1e293b;
1237
+ border-color: #334155;
1238
+ color: #e2e8f0;
1239
+ }
1240
+
1241
+ .ds-theme-dark .ds-toolbar-btn:hover {
1242
+ background: #0f172a;
1243
+ }
1244
+ `, e = document.createElement("style");
1245
+ e.id = "deepspot-sdk-styles", e.textContent = p, document.head.appendChild(e);
1246
+ } catch (p) {
1247
+ }
1248
+ }
1249
+ }
1250
+ function R(p) {
1251
+ if (typeof p == "string") {
1252
+ const e = document.querySelector(p);
1253
+ if (!e) throw new Error(`Deepspot SDK: container "${p}" not found in DOM`);
1254
+ return e;
1255
+ }
1256
+ return p;
1257
+ }
1258
+ class O {
1259
+ constructor(e) {
1260
+ if (!e.apiKey) throw new Error("Deepspot SDK: apiKey is required");
1261
+ if (!e.baseUrl) throw new Error("Deepspot SDK: baseUrl is required");
1262
+ this.apiClient = new D(e.baseUrl, e.apiKey), j();
1263
+ }
1264
+ // ── Embed full dashboard ────────────────────────────────────────────────────
1265
+ async embedDashboard(e) {
1266
+ var a, d, o, c, l;
1267
+ const t = R(e.container), r = (a = e.embedLevel) != null ? a : "dashboard", s = (d = e.theme) != null ? d : "light";
1268
+ t.style.height = e.height || "600px", t.style.display = "block";
1269
+ const i = this.createRoot(t, s);
1270
+ this.showLoading(i);
1271
+ try {
1272
+ const n = await this.apiClient.getEmbedToken({
1273
+ dashboardId: e.dashboardId,
1274
+ embedType: "dashboard",
1275
+ embedLevel: r,
1276
+ userId: e.userId,
1277
+ tenantId: e.tenantId
1278
+ }), h = await this.apiClient.getDashboardRender(
1279
+ e.dashboardId,
1280
+ n,
1281
+ {
1282
+ embedLevel: r,
1283
+ pageId: e.pageId,
1284
+ tabId: e.tabId,
1285
+ filters: e.filters || {}
1286
+ }
1287
+ );
1288
+ let b;
1289
+ const g = new A(i, {
1290
+ embedLevel: r,
1291
+ theme: s,
1292
+ hideFilters: (o = e.hideFilters) != null ? o : !1,
1293
+ hideExport: (c = e.hideExport) != null ? c : !1,
1294
+ initialFilters: e.filters || {},
1295
+ onFilterChange: e.onFilterChange,
1296
+ onReady: e.onReady,
1297
+ onTabSwitch: async (m, v) => {
1298
+ var x, w, T;
1299
+ (x = e.onTabSwitch) == null || x.call(e, m, v);
1300
+ try {
1301
+ const u = await this.apiClient.getDashboardRender(
1302
+ e.dashboardId,
1303
+ n,
1304
+ {
1305
+ embedLevel: r,
1306
+ pageId: m,
1307
+ tabId: v,
1308
+ filters: (T = (w = b == null ? void 0 : b.getActiveFilters()) != null ? w : e.filters) != null ? T : {}
1309
+ }
1310
+ );
1311
+ g.update(u);
1312
+ } catch (u) {
1313
+ console.error("Deepspot SDK: tab fetch failed", u);
1314
+ }
1315
+ }
1316
+ });
1317
+ return g.render(h), b = new k({
1318
+ dashboardId: e.dashboardId,
1319
+ token: n,
1320
+ embedType: "dashboard",
1321
+ embedLevel: r,
1322
+ activePageId: h.activePage,
1323
+ activeTabId: h.activeTab,
1324
+ activeFilters: e.filters || {},
1325
+ apiClient: this.apiClient,
1326
+ renderer: g,
1327
+ onFilterChange: e.onFilterChange
1328
+ }), b;
1329
+ } catch (n) {
1330
+ throw this.showError(i, (n == null ? void 0 : n.message) || "Failed to load dashboard"), (l = e.onError) == null || l.call(e, (n == null ? void 0 : n.message) || "Failed to load dashboard"), n;
1331
+ }
1332
+ }
1333
+ // ── Embed single report (component) ────────────────────────────────────────
1334
+ async embedReport(e) {
1335
+ var s;
1336
+ const t = R(e.container);
1337
+ t.style.height = e.height || "400px", t.style.display = "block";
1338
+ const r = this.createRoot(t, e.theme || "light");
1339
+ this.showLoading(r);
1340
+ try {
1341
+ const i = await this.apiClient.getEmbedToken({
1342
+ dashboardId: e.dashboardId,
1343
+ embedType: "report",
1344
+ componentId: e.componentId,
1345
+ userId: e.userId,
1346
+ tenantId: e.tenantId
1347
+ }), a = await this.apiClient.getReportRender(
1348
+ e.dashboardId,
1349
+ e.componentId,
1350
+ i,
1351
+ { filters: e.filters || {} }
1352
+ ), d = new P(r, e.theme || "light");
1353
+ return d.render(a, e.onReady), new k({
1354
+ dashboardId: e.dashboardId,
1355
+ componentId: e.componentId,
1356
+ token: i,
1357
+ embedType: "report",
1358
+ activeFilters: e.filters || {},
1359
+ apiClient: this.apiClient,
1360
+ renderer: d
1361
+ });
1362
+ } catch (i) {
1363
+ throw this.showError(r, (i == null ? void 0 : i.message) || "Failed to load report"), (s = e.onError) == null || s.call(e, (i == null ? void 0 : i.message) || "Failed to load report"), i;
1364
+ }
1365
+ }
1366
+ // ── Private helpers ─────────────────────────────────────────────────────────
1367
+ createRoot(e, t) {
1368
+ e.innerHTML = "";
1369
+ const r = document.createElement("div");
1370
+ return r.className = `ds-embed-root ds-theme-${t}`, r.style.width = "100%", r.style.height = "100%", e.appendChild(r), r;
1371
+ }
1372
+ showLoading(e) {
1373
+ e.innerHTML = `
1374
+ <div class="ds-embed-loading">
1375
+ <div class="ds-embed-spinner"></div>
1376
+ <div class="ds-embed-loading-text">Loading…</div>
1377
+ </div>
1378
+ `;
1379
+ }
1380
+ showError(e, t) {
1381
+ e.innerHTML = `
1382
+ <div class="ds-embed-error">
1383
+ <div class="ds-embed-error-icon">⚠</div>
1384
+ <div>${t}</div>
1385
+ <div class="ds-embed-error-message">Check your SDK configuration.</div>
1386
+ </div>
1387
+ `;
1388
+ }
1389
+ }
1390
+ export {
1391
+ O as DeepspotSDK
1392
+ };