UNPKG

datajunction-ui

Version:
1,243 lines (1,242 loc) โ€ข 77.6 kB
import { jsxs as i, jsx as t, Fragment as Re } from "react/jsx-runtime"; import { useState as b, useRef as ee, useEffect as Q, useMemo as de, useCallback as L, memo as Tt, useContext as jt, Suspense as zt, lazy as Kt } from "react"; import { useLocation as Bt, useNavigate as qt } from "react-router-dom"; import { ac as Wt, aP as Ut, ag as St, cd as It, D as Ot } from "./index-n79cxNwC.js"; import { P as Vt, M as Gt, Q as Qt } from "./PreAggDetailsPanel-UslxH4LA.js"; import { R as Dt, c as xt, X as Pt, Y as _t, a as Ht, B as Yt } from "./BarChart-JyKNSAsC.js"; import { a as Xt, L as Jt } from "./LineChart-B9ulwx-b.js"; const Zt = [ { value: null, label: "Auto" }, { value: "druid", label: "Druid" }, { value: "trino", label: "Trino" } ], wt = "application/x-dj-dimension", Lt = (l, d) => (l.trim() ? l.trimEnd() + " AND " : "") + d; function Ft({ text: l }) { return /* @__PURE__ */ t("span", { className: "chip-label chip-label-clip", title: l, children: /* @__PURE__ */ t("bdi", { className: "chip-label-inner", children: l }) }); } function en({ metrics: l, selectedMetrics: d, onMetricsChange: N, dimensions: _, selectedDimensions: w, onDimensionsChange: M, loading: C, cubes: v = [], onLoadCubePreset: k, loadedCubeName: g = null, // Managed by parent for URL persistence onClearSelection: E, filters: h = [], onFiltersChange: K, selectedEngine: F = null, onEngineChange: R, onRunQuery: $, canRunQuery: j = !1, queryLoading: W = !1, compatibleMetrics: O = null // Set<string> of compatible metric names, or null if no filter }) { const [I, ue] = b(""), [S, Ne] = b(""), [z, ge] = b(/* @__PURE__ */ new Set()), [ze, H] = b(/* @__PURE__ */ new Set()), [Y, oe] = b(/* @__PURE__ */ new Set()), [ke, Ce] = b(!1), [V, Pe] = b(""), [_e, Le] = b(!1), [te, x] = b(!1), [D, B] = b(""), [X, ne] = b(!1), [be, $e] = b(35), [Se, Ae] = b(65), [ae, Te] = b(85), we = ee(""), Ke = ee(null), Be = ee(null), Fe = ee(null), T = ee(null), me = ee(null), he = ee(null), Me = ee({ split1: 35, split2: 65, split3: 85 }); Q(() => { Me.current = { split1: be, split2: Se, split3: ae }; }, [be, Se, ae]); const ve = 8, De = de(() => g ? v.find((e) => e.name === g) || { name: g } : null, [g, v]); Q(() => { const e = (r) => { Ke.current && !Ke.current.contains(r.target) && Ce(!1); }; return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e); }, []); const We = de(() => { const e = V.toLowerCase().trim(); return e ? v.filter((r) => { const m = r.name || "", f = r.display_name || ""; return m.toLowerCase().includes(e) || f.toLowerCase().includes(e); }) : v; }, [v, V]), Ee = (e) => { const r = e.split("."); return r[r.length - 1]; }, Xe = (e) => { const r = e.split("."); return r.length > 1 ? r.slice(0, -1).join(".") : "default"; }, xe = de(() => { const e = {}; return l.forEach((r) => { const m = Xe(r); e[m] || (e[m] = []), e[m].push(r); }), e; }, [l]), { filteredGroups: ye, sortedNamespaces: je } = de(() => { const e = I.trim().toLowerCase(); if (!e) { const f = Object.keys(xe).sort(); return { filteredGroups: xe, sortedNamespaces: f }; } const r = {}; Object.entries(xe).forEach(([f, p]) => { const s = p.filter((n) => n.toLowerCase().includes(e)); s.length > 0 && (s.sort((n, a) => { const o = Ee(n).toLowerCase(), c = Ee(a).toLowerCase(), u = o.startsWith(e), A = c.startsWith(e); return u && !A ? -1 : !u && A ? 1 : o.localeCompare(c); }), r[f] = s); }); const m = Object.keys(r).sort((f, p) => { const s = f.toLowerCase(), n = p.toLowerCase(), a = s.startsWith(e), o = n.startsWith(e); if (a && !o) return -1; if (!a && o) return 1; const c = s.includes(e), u = n.includes(e); if (c && !u) return -1; if (!c && u) return 1; const A = r[f].length, q = r[p].length; return A !== q ? q - A : s.localeCompare(n); }); return { filteredGroups: r, sortedNamespaces: m }; }, [xe, I]); Q(() => { const e = I.trim(), r = we.current; e && e !== r && ge(new Set(je)), we.current = e; }, [I, je]); const y = de(() => { const e = /* @__PURE__ */ new Map(); return _.forEach((r) => { var f, p; if (!r.name) return; const m = e.get(r.name); (!m || (((f = r.path) == null ? void 0 : f.length) || 0) < (((p = m.path) == null ? void 0 : p.length) || 1 / 0)) && e.set(r.name, r); }), Array.from(e.values()); }, [_]), Je = (e) => { const r = e.match(/\[([^\]]+)\]$/); return r ? r[1] : null; }, J = (e) => e ? "via " + e.replace(/->/g, " โ†’ ") : "direct", pe = (e) => e ? e.split("->").length : 0, U = de(() => { const e = S.trim().toLowerCase(), r = /* @__PURE__ */ new Map(); y.forEach((f) => { var o; if (!f.name) return; const p = ((o = f.path) == null ? void 0 : o.length) > 0 ? f.path[f.path.length - 1] : f.name.split(".").slice(0, -1).join("."), s = Math.max(0, f.path ? f.path.length - 1 : 0), n = Je(f.name); r.has(p) || r.set(p, { nodeKey: p, minDistance: s, rolePathMap: /* @__PURE__ */ new Map() }); const a = r.get(p); a.minDistance = Math.min(a.minDistance, s), a.rolePathMap.has(n) || a.rolePathMap.set(n, { roleKey: n, dimensions: [] }), a.rolePathMap.get(n).dimensions.push(f); }); let m = Array.from(r.values()).map((f) => { const p = Array.from(f.rolePathMap.values()).sort( (s, n) => pe(s.roleKey) - pe(n.roleKey) || (s.roleKey || "").localeCompare(n.roleKey || "") ); return p.forEach((s) => { s.dimensions.sort((n, a) => n.name.localeCompare(a.name)); }), { nodeKey: f.nodeKey, minDistance: f.minDistance, rolePaths: p, totalCount: p.reduce((s, n) => s + n.dimensions.length, 0) }; }); return e && (m = m.map((f) => ({ ...f, rolePaths: f.rolePaths.map((p) => ({ ...p, dimensions: p.dimensions.filter((s) => s.name.toLowerCase().includes(e) || s.name.split(".").pop().toLowerCase().includes(e)) })).filter( (p) => p.dimensions.length > 0 || (p.roleKey || "").toLowerCase().includes(e) ) })).filter( (f) => f.rolePaths.length > 0 || f.nodeKey.toLowerCase().includes(e) ).map((f) => ({ ...f, totalCount: f.rolePaths.reduce( (p, s) => p + s.dimensions.length, 0 ) }))), m.sort( (f, p) => f.minDistance - p.minDistance || f.nodeKey.localeCompare(p.nodeKey) ), m; }, [y, S]); Q(() => { U.length !== 0 && (S.trim() ? (H(new Set(U.map((e) => e.nodeKey))), oe( new Set( U.flatMap( (e) => e.rolePaths.map((r) => `${e.nodeKey}::${r.roleKey}`) ) ) )) : (H((e) => e.size > 0 ? e : new Set( U.filter((r) => r.minDistance === 0).map((r) => r.nodeKey) )), oe((e) => e.size > 0 ? e : new Set( U.filter((r) => r.minDistance === 0).map((r) => `${r.nodeKey}::null`) )))); }, [U, S]); const se = (e) => (e || "").split(".").slice(-2).join("."), Ze = (e) => { ge((r) => { const m = new Set(r); return m.has(e) ? m.delete(e) : m.add(e), m; }); }, et = (e) => { H((r) => { const m = new Set(r); return m.has(e) ? m.delete(e) : m.add(e), m; }); }, tt = (e, r) => { const m = `${e}::${r}`; oe((f) => { const p = new Set(f); return p.has(m) ? p.delete(m) : p.add(m), p; }); }, nt = (e) => e.split(".").pop(), qe = L((e, r) => { e.preventDefault(); const m = r === 1 ? Me.current.split1 : r === 2 ? Me.current.split2 : Me.current.split3; he.current = { divider: r, startY: e.clientY, startSplit: m }; }, []); Q(() => { const e = (m) => { if (!he.current || !me.current) return; const f = me.current.getBoundingClientRect().height, p = (m.clientY - he.current.startY) / f * 100, { divider: s, startSplit: n } = he.current, { split1: a, split2: o, split3: c } = Me.current; s === 1 ? $e(Math.max(10, Math.min(o - 15, n + p))) : s === 2 ? Ae( Math.max(a + 15, Math.min(c - 10, n + p)) ) : Te(Math.max(o + 10, Math.min(95, n + p))); }, r = () => { he.current = null; }; return window.addEventListener("mousemove", e), window.addEventListener("mouseup", r), () => { window.removeEventListener("mousemove", e), window.removeEventListener("mouseup", r); }; }, []); const Oe = (e) => { d.includes(e) ? N(d.filter((r) => r !== e)) : N([...d, e]); }, at = (e) => { N(d.filter((r) => r !== e)); }, st = (e) => { w.includes(e) ? M(w.filter((r) => r !== e)) : M([...w, e]); }, rt = (e) => { M(w.filter((r) => r !== e)); }, lt = (e, r) => { const m = [.../* @__PURE__ */ new Set([...d, ...r])]; N(m); }, ot = (e, r) => { N(d.filter((m) => !r.includes(m))); }, it = (e) => { k && k(e.name), Ce(!1), Pe(""); }, ct = () => { E ? E() : (N([]), M([])); }, Ve = () => { const e = D.trim(); e && !h.includes(e) && K && (K([...h, e]), B("")); }, dt = (e) => { e.key === "Enter" && (e.preventDefault(), Ve()); }, ut = (e) => { K && K(h.filter((r) => r !== e)); }, mt = (e) => { var r; B((m) => Lt(m, e)), K && K(h.filter((m) => m !== e)), (r = T.current) == null || r.focus(); }, Ge = (e) => { var r; B((m) => Lt(m, e + " ")), (r = T.current) == null || r.focus(); }, ht = (e, r) => { e.preventDefault(), e.stopPropagation(), Ge(r); }, Qe = (e, r) => { e.dataTransfer.setData(wt, r), e.dataTransfer.setData("text/plain", r), e.dataTransfer.effectAllowed = "copy"; }, pt = (e) => { e.dataTransfer.types.includes(wt) && (e.preventDefault(), e.dataTransfer.dropEffect = "copy", X || ne(!0)); }, ft = (e) => { e.currentTarget.contains(e.relatedTarget) || ne(!1); }, gt = (e) => { const r = e.dataTransfer.getData(wt); r && (e.preventDefault(), Ge(r)), ne(!1); }; return /* @__PURE__ */ i("div", { className: "selection-panel", children: [ v.length > 0 && /* @__PURE__ */ i("div", { className: "cube-preset-section", ref: Ke, children: [ /* @__PURE__ */ i("div", { className: "preset-row", children: [ /* @__PURE__ */ i( "button", { className: `preset-button ${De ? "has-preset" : ""}`, onClick: () => Ce(!ke), children: [ /* @__PURE__ */ t("span", { className: "preset-icon", children: De ? "๐Ÿ“ฆ" : "๐Ÿ“‚" }), /* @__PURE__ */ t("span", { className: "preset-label", children: De ? De.display_name || (De.name ? De.name.split(".").pop() : "Cube") : "Load from Cube" }), /* @__PURE__ */ t("span", { className: "dropdown-arrow", children: ke ? "โ–ฒ" : "โ–ผ" }) ] } ), (d.length > 0 || w.length > 0) && /* @__PURE__ */ t("button", { className: "clear-all-btn", onClick: ct, children: "Clear" }) ] }), ke && /* @__PURE__ */ i("div", { className: "cube-dropdown", children: [ /* @__PURE__ */ t( "input", { type: "text", className: "cube-search", placeholder: "Search cubes...", value: V, onChange: (e) => Pe(e.target.value), autoFocus: !0 } ), /* @__PURE__ */ t("div", { className: "cube-list", children: We.length === 0 ? /* @__PURE__ */ t("div", { className: "cube-empty", children: V ? "No cubes match your search" : "No cubes available" }) : We.map((e) => /* @__PURE__ */ i( "button", { className: `cube-option ${g === e.name ? "selected" : ""}`, onClick: () => it(e), children: [ /* @__PURE__ */ t("span", { className: "cube-name", children: e.display_name || (e.name ? e.name.split(".").pop() : "Unknown") }), /* @__PURE__ */ t("span", { className: "cube-info", children: e.name }), g === e.name && /* @__PURE__ */ t("span", { className: "cube-selected-icon", children: "โœ“" }) ] }, e.name )) }) ] }) ] }), /* @__PURE__ */ i("div", { className: "resizable-sections", ref: me, children: [ /* @__PURE__ */ i("div", { className: "selection-section", style: { flex: be }, children: [ /* @__PURE__ */ i("div", { className: "section-header", children: [ /* @__PURE__ */ t("h3", { children: "Metrics" }), /* @__PURE__ */ i("span", { className: "selection-count", children: [ d.length, " selected" ] }) ] }), /* @__PURE__ */ i( "div", { className: "combobox-input", onClick: () => { var e; return (e = Be.current) == null ? void 0 : e.focus(); }, children: [ d.length > 0 && /* @__PURE__ */ t( "div", { className: `combobox-chips ${d.length > ve ? _e ? "expanded" : "collapsed" : ""}`, children: d.map((e) => /* @__PURE__ */ i("span", { className: "selected-chip metric-chip", children: [ /* @__PURE__ */ t(Ft, { text: Ee(e) }), /* @__PURE__ */ t( "button", { className: "chip-remove", onClick: (r) => { r.stopPropagation(), at(e); }, title: `Remove ${Ee(e)}`, children: "ร—" } ) ] }, e)) } ), /* @__PURE__ */ i("div", { className: "combobox-input-row", children: [ /* @__PURE__ */ t( "input", { ref: Be, type: "text", className: "combobox-search", placeholder: "Search metrics...", value: I, onChange: (e) => ue(e.target.value), onClick: (e) => e.stopPropagation() } ), d.length > ve && /* @__PURE__ */ t( "button", { className: "combobox-action", onClick: (e) => { e.stopPropagation(), Le(!_e); }, children: _e ? "Show less" : "Show all" } ), d.length > 0 && /* @__PURE__ */ t( "button", { className: "combobox-action", onClick: (e) => { e.stopPropagation(), N([]); }, children: "Clear" } ) ] }) ] } ), /* @__PURE__ */ i("div", { className: "selection-list", children: [ je.map((e) => { const r = ye[e], m = z.has(e), f = r.filter( (p) => d.includes(p) ).length; return /* @__PURE__ */ i("div", { className: "namespace-group", children: [ /* @__PURE__ */ i( "div", { className: "namespace-header", onClick: () => Ze(e), children: [ /* @__PURE__ */ t("span", { className: "expand-icon", children: m ? "โ–ผ" : "โ–ถ" }), /* @__PURE__ */ t("span", { className: "namespace-name", children: e }), /* @__PURE__ */ i("span", { className: "namespace-count", children: [ f > 0 && /* @__PURE__ */ t("span", { className: "selected-badge", children: f }), r.length ] }) ] } ), m && /* @__PURE__ */ i("div", { className: "namespace-items", children: [ /* @__PURE__ */ i("div", { className: "namespace-actions", children: [ /* @__PURE__ */ t( "button", { type: "button", className: "select-all-btn", onClick: () => lt(e, r), children: "Select all" } ), /* @__PURE__ */ t( "button", { type: "button", className: "select-all-btn", onClick: () => ot(e, r), children: "Clear" } ) ] }), r.map((p) => { const s = O !== null && !O.has(p) && !d.includes(p); return /* @__PURE__ */ i( "label", { className: `selection-item${s ? " metric-incompatible" : ""}`, title: s ? "Not compatible with selected dimensions" : p, children: [ /* @__PURE__ */ t( "input", { type: "checkbox", checked: d.includes(p), onChange: () => Oe(p) } ), /* @__PURE__ */ t("span", { className: "item-name", children: Ee(p) }), O !== null && O.has(p) && !d.includes(p) && /* @__PURE__ */ t( "span", { className: "metric-compatible-badge", title: "Compatible with selected dimensions", children: "โœ“" } ) ] }, p ); }) ] }) ] }, e); }), je.length === 0 && /* @__PURE__ */ t("div", { className: "empty-list", children: I ? "No metrics match your search" : "No metrics available" }) ] }) ] }), /* @__PURE__ */ t( "div", { className: "section-divider draggable-divider", onMouseDown: (e) => qe(e, 1) } ), /* @__PURE__ */ i("div", { className: "selection-section", style: { flex: Se - be }, children: [ /* @__PURE__ */ i("div", { className: "section-header", children: [ /* @__PURE__ */ t("h3", { children: "Dimensions" }), /* @__PURE__ */ i("span", { className: "selection-count", children: [ w.length, " selected", _.length > 0 && ` / ${_.length} available` ] }) ] }), d.length === 0 ? /* @__PURE__ */ t("div", { className: "empty-list hint", children: "Select metrics to see available dimensions" }) : C ? /* @__PURE__ */ t("div", { className: "empty-list", children: "Loading dimensions..." }) : /* @__PURE__ */ i(Re, { children: [ /* @__PURE__ */ i( "div", { className: "combobox-input", onClick: () => { var e; return (e = Fe.current) == null ? void 0 : e.focus(); }, children: [ w.length > 0 && /* @__PURE__ */ t( "div", { className: `combobox-chips ${w.length > ve ? te ? "expanded" : "collapsed" : ""}`, children: w.map((e) => /* @__PURE__ */ i( "span", { className: "selected-chip dimension-chip draggable-dim", title: `${e} โ€” drag to Filters to filter on it`, draggable: !0, onDragStart: (r) => Qe(r, e), children: [ /* @__PURE__ */ t(Ft, { text: se(e) }), /* @__PURE__ */ t( "button", { className: "chip-remove", onClick: (r) => { r.stopPropagation(), rt(e); }, title: `Remove ${se(e)}`, children: "ร—" } ) ] }, e )) } ), /* @__PURE__ */ i("div", { className: "combobox-input-row", children: [ /* @__PURE__ */ t( "input", { ref: Fe, type: "text", className: "combobox-search", placeholder: "Search dimensions...", value: S, onChange: (e) => Ne(e.target.value), onClick: (e) => e.stopPropagation() } ), w.length > ve && /* @__PURE__ */ t( "button", { className: "combobox-action", onClick: (e) => { e.stopPropagation(), x(!te); }, children: te ? "Show less" : "Show all" } ), w.length > 0 && /* @__PURE__ */ t( "button", { className: "combobox-action", onClick: (e) => { e.stopPropagation(), M([]); }, children: "Clear" } ) ] }) ] } ), /* @__PURE__ */ i("div", { className: "selection-list dimensions-list", children: [ U.map((e) => { const r = ze.has(e.nodeKey); return /* @__PURE__ */ i("div", { className: "dim-group", children: [ /* @__PURE__ */ i( "div", { className: "dim-group-header", onClick: () => et(e.nodeKey), title: e.nodeKey, children: [ /* @__PURE__ */ t("span", { className: "expand-icon", children: r ? "โ–ผ" : "โ–ถ" }), /* @__PURE__ */ t("span", { className: "dim-group-name", children: nt(e.nodeKey) }), /* @__PURE__ */ t("span", { className: "dim-group-count", children: e.totalCount }) ] } ), r && e.rolePaths.map((m) => { const f = `${e.nodeKey}::${m.roleKey}`, p = Y.has(f), s = pe(m.roleKey), n = s === 0 ? "direct" : `${s} hop${s > 1 ? "s" : ""}`; return /* @__PURE__ */ i("div", { className: "dim-role-group", children: [ /* @__PURE__ */ i( "div", { className: "dim-role-header", onClick: () => tt(e.nodeKey, m.roleKey), children: [ /* @__PURE__ */ t("span", { className: "expand-icon", children: p ? "โ–ผ" : "โ–ถ" }), /* @__PURE__ */ t("span", { className: "dim-role-label", children: J(m.roleKey) }), /* @__PURE__ */ t("span", { className: "dim-group-meta", children: n }), /* @__PURE__ */ t("span", { className: "dim-group-count", children: m.dimensions.length }) ] } ), p && m.dimensions.map((a) => /* @__PURE__ */ i( "label", { className: "selection-item dimension-item dim-role-item draggable-dim", title: `${a.name} โ€” drag to Filters to filter on it`, draggable: !0, onDragStart: (o) => Qe(o, a.name), children: [ /* @__PURE__ */ t( "input", { type: "checkbox", checked: w.includes( a.name ), onChange: () => st(a.name) } ), /* @__PURE__ */ t("div", { className: "dimension-info", children: /* @__PURE__ */ t("span", { className: "item-name", children: se( a.name.replace(/\[[^\]]*\]$/, "") ) }) }), /* @__PURE__ */ t( "button", { className: "dim-filter-btn", title: `Add "${a.name}" to filters`, onClick: (o) => ht(o, a.name), children: "+ filter" } ) ] }, a.name )) ] }, f); }) ] }, e.nodeKey); }), U.length === 0 && /* @__PURE__ */ t("div", { className: "empty-list", children: S ? "No dimensions match your search" : "No shared dimensions" }) ] }) ] }) ] }), /* @__PURE__ */ t( "div", { className: "section-divider draggable-divider", onMouseDown: (e) => qe(e, 2) } ), /* @__PURE__ */ i( "div", { className: `selection-section filters-section${X ? " drop-active" : ""}`, style: { flex: ae - Se }, onDragOver: pt, onDragLeave: ft, onDrop: gt, children: [ /* @__PURE__ */ i("div", { className: "section-header", children: [ /* @__PURE__ */ t("h3", { children: "Filters" }), /* @__PURE__ */ i("span", { className: "selection-count", children: [ h.length, " applied" ] }) ] }), X && /* @__PURE__ */ t("div", { className: "filter-drop-hint", children: "Drop to filter on this dimension" }), h.length > 0 && /* @__PURE__ */ t("div", { className: "filter-chips-container", children: h.map((e, r) => /* @__PURE__ */ i("span", { className: "filter-chip", children: [ /* @__PURE__ */ t( "button", { type: "button", className: "filter-chip-text filter-chip-edit", onClick: () => mt(e), title: `${e} โ€” click to edit`, children: e } ), /* @__PURE__ */ t( "button", { className: "filter-chip-remove", onClick: () => ut(e), title: "Remove filter", children: "ร—" } ) ] }, r)) }), /* @__PURE__ */ i("div", { className: "filter-input-container", children: [ /* @__PURE__ */ t( "input", { ref: T, type: "text", className: "filter-input", placeholder: "e.g. v3.date.date_id >= '2024-01-01'", value: D, onChange: (e) => B(e.target.value), onKeyDown: dt } ), /* @__PURE__ */ t( "button", { className: "filter-add-btn", onClick: Ve, disabled: !D.trim(), children: "Add" } ) ] }) ] } ), /* @__PURE__ */ t( "div", { className: "section-divider draggable-divider", onMouseDown: (e) => qe(e, 3) } ), /* @__PURE__ */ i( "div", { className: "selection-section engine-run-section", style: { flex: 100 - ae }, children: [ /* @__PURE__ */ i("div", { className: "engine-section", children: [ /* @__PURE__ */ t("span", { className: "engine-label", children: "Engine" }), /* @__PURE__ */ t("div", { className: "engine-pills", children: Zt.map(({ value: e, label: r }) => /* @__PURE__ */ t( "button", { className: `engine-pill${F === e ? " active" : ""}`, onClick: () => R && R(e), children: r }, r )) }) ] }), /* @__PURE__ */ i("div", { className: "run-query-section", children: [ /* @__PURE__ */ t( "button", { className: "run-query-btn", onClick: $, disabled: !j || W, children: W ? /* @__PURE__ */ i(Re, { children: [ /* @__PURE__ */ t("span", { className: "spinner small" }), "Running..." ] }) : /* @__PURE__ */ i(Re, { children: [ /* @__PURE__ */ t("span", { className: "run-icon", children: "โ–ถ" }), "Run Query" ] }) } ), !j && /* @__PURE__ */ t("span", { className: "run-hint", children: "Select at least one metric to run a query" }) ] }) ] } ) ] }) ] }); } Wt.registerLanguage("sql", It); const yt = [ "#60a5fa", "#34d399", "#fbbf24", "#f87171", "#a78bfa", "#22d3ee", "#fb923c" ], tn = 2; function Mt(l) { const d = l.name.toLowerCase(), N = (l.type || "").toLowerCase(); return N.includes("date") || N.includes("timestamp") || N.includes("time") || d === "date" || d === "day" || d === "week" || d === "month" || d === "year" || d === "quarter" || d.endsWith("_date") || d.endsWith("_day") || d.endsWith("_week") || d.endsWith("_month") || d.endsWith("_year") || d.endsWith("_at") || d.startsWith("date") || d.startsWith("ds"); } function Et(l) { const d = (l.type || "").toLowerCase(); return d.includes("int") || d.includes("float") || d.includes("double") || d.includes("decimal") || d.includes("numeric") || d.includes("real") || d.includes("number"); } function nn(l, d) { if (!l.length || !d.length) return null; const N = l.map((v, k) => ({ ...v, idx: k })), _ = N.filter((v) => Mt(v)), w = N.filter((v) => Et(v)), C = N.filter((v) => !Et(v)).filter((v) => !Mt(v)); if (_.length > 0) { const v = _[0], k = w.filter((g) => g.idx !== v.idx); if (k.length > 0) return C.length === 1 ? { type: "line", xCol: v, groupByCol: C[0], metricCols: k } : { type: "line", xCol: v, metricCols: k }; } if (C.length > 0 && w.length > 0) return C.length === 1 ? { type: "bar", xCol: C[0], metricCols: w } : C.length === 2 ? { type: "bar", xCol: C[0], groupByCol: C[1], metricCols: w } : { type: "bar", xCol: C[0], metricCols: w }; if (w.length > 1) { const v = w[0], k = w.slice(1); return { type: "line", xCol: v, metricCols: k }; } return w.length > 0 ? { type: "kpi", metricCols: w } : null; } const an = 50; function sn(l, d, N, _, w) { const M = N.idx, C = _.idx, v = w.map((R) => R.idx), k = {}; for (const R of l) { const $ = String(R[C] ?? "(null)"); k[$] = (k[$] || 0) + (Number(R[v[0]]) || 0); } const g = Object.entries(k).sort((R, $) => $[1] - R[1]).slice(0, an).map(([R]) => R), E = new Set(g), h = w.map(() => ({})); for (const R of l) { const $ = String(R[C] ?? "(null)"); if (!E.has($)) continue; const j = R[M], W = String(j ?? "(null)"); for (let O = 0; O < w.length; O++) { const I = h[O]; I[W] || (I[W] = { [N.name]: j }), I[W][$] = R[v[O]]; } } const K = (R, $) => { const j = R[N.name], W = $[N.name]; return j === null && W === null ? 0 : j === null ? 1 : W === null ? -1 : typeof j == "number" && typeof W == "number" ? j - W : String(j).localeCompare(String(W)); }; return { pivotedByMetric: w.map((R, $) => { const j = Object.values(h[$]); return j.sort(K), { col: R, data: j }; }), groupValues: g }; } function rn(l, d, N) { const _ = d.map((M) => { const C = {}; return l.forEach((v, k) => { C[v.name] = M[k]; }), C; }), w = N.name; return _.sort((M, C) => { const v = M[w], k = C[w]; return v === null && k === null ? 0 : v === null ? 1 : k === null ? -1 : typeof v == "number" && typeof k == "number" ? v - k : String(v).localeCompare(String(k)); }), _; } function Rt(l) { return Math.abs(l) >= 1e9 ? (l / 1e9).toFixed(1) + "B" : Math.abs(l) >= 1e6 ? (l / 1e6).toFixed(1) + "M" : Math.abs(l) >= 1e3 ? (l / 1e3).toFixed(1) + "K" : l; } function ln({ rows: l, metricCols: d }) { const N = l[0] || []; return /* @__PURE__ */ t("div", { className: "kpi-cards", children: d.map((_) => { const w = N[_.idx], M = w == null ? "โ€”" : typeof w == "number" ? w.toLocaleString(void 0, { maximumFractionDigits: 4 }) : String(w); return /* @__PURE__ */ i("div", { className: "kpi-card", children: [ /* @__PURE__ */ t("div", { className: "kpi-label", children: _.name }), /* @__PURE__ */ t("div", { className: "kpi-value", children: M }), _.type && /* @__PURE__ */ t("div", { className: "kpi-type", children: _.type }) ] }, _.idx); }) }); } const $t = { top: 8, right: 24, left: 8, bottom: 40 }, He = { fontSize: 11, fill: "#64748b" }, At = { fontSize: 12, border: "1px solid #e2e8f0" }, on = { zIndex: 9999 }, Ye = Tt(function({ type: d, xCol: N, metricCols: _, seriesKeys: w, chartData: M, seriesColors: C = yt }) { const v = M.length <= 60, k = w || _.map((E) => E.name), g = d === "line" ? "preserveStartEnd" : Math.max(0, Math.ceil(M.length / 20) - 1); return d === "line" ? /* @__PURE__ */ t(Dt, { width: "100%", height: "100%", children: /* @__PURE__ */ i(Xt, { data: M, margin: $t, children: [ /* @__PURE__ */ t(xt, { strokeDasharray: "3 3", stroke: "#e2e8f0" }), /* @__PURE__ */ t( Pt, { dataKey: N.name, tick: He, angle: -35, textAnchor: "end", interval: g } ), /* @__PURE__ */ t(_t, { tickFormatter: Rt, tick: He, width: 60 }), /* @__PURE__ */ t( St, { contentStyle: At, wrapperStyle: on } ), k.map((E, h) => /* @__PURE__ */ t( Jt, { type: "monotone", dataKey: E, stroke: C[h % C.length], dot: v, strokeWidth: 2, isAnimationActive: !1 }, E )) ] }) }) : /* @__PURE__ */ t(Dt, { width: "100%", height: "100%", children: /* @__PURE__ */ i(Ht, { data: M, margin: $t, children: [ /* @__PURE__ */ t(xt, { strokeDasharray: "3 3", stroke: "#e2e8f0" }), /* @__PURE__ */ t( Pt, { dataKey: N.name, tick: He, angle: -35, textAnchor: "end", interval: g } ), /* @__PURE__ */ t(_t, { tickFormatter: Rt, tick: He, width: 60 }), /* @__PURE__ */ t(St, { contentStyle: At }), k.map((E, h) => /* @__PURE__ */ t( Yt, { dataKey: E, fill: C[h % C.length], isAnimationActive: !1 }, E )) ] }) }); }), cn = Tt(function({ chartConfig: d, chartData: N, pivotedByMetric: _, groupValues: w, rows: M, columns: C }) { if (!d) return /* @__PURE__ */ t("div", { className: "chart-no-data", children: "No chartable data detected" }); if (d.type === "kpi") return /* @__PURE__ */ t(ln, { rows: M, metricCols: d.metricCols }); const { type: v, xCol: k, metricCols: g } = d; return _ && _.length > 1 ? /* @__PURE__ */ t("div", { className: "small-multiples", children: _.map(({ col: E, data: h }) => /* @__PURE__ */ i("div", { className: "small-multiple", children: [ /* @__PURE__ */ t("div", { className: "small-multiple-label", children: E.name }), /* @__PURE__ */ t("div", { className: "small-multiple-chart", children: /* @__PURE__ */ t( Ye, { type: v, xCol: k, metricCols: [E], seriesKeys: w, chartData: h } ) }) ] }, E.idx)) }) : w ? /* @__PURE__ */ t( Ye, { type: v, xCol: k, metricCols: g, seriesKeys: w, chartData: N } ) : g.length > tn ? /* @__PURE__ */ t("div", { className: "small-multiples", children: g.map((E, h) => /* @__PURE__ */ i("div", { className: "small-multiple", children: [ /* @__PURE__ */ t("div", { className: "small-multiple-label", children: E.name }), /* @__PURE__ */ t("div", { className: "small-multiple-chart", children: /* @__PURE__ */ t( Ye, { type: v, xCol: k, metricCols: [E], chartData: N, seriesColors: [yt[h % yt.length]] } ) }) ] }, E.idx)) }) : /* @__PURE__ */ t( Ye, { type: v, xCol: k, metricCols: g, chartData: N } ); }); function dn({ sql: l, results: d, loading: N, error: _, elapsedTime: w, onBackToPlan: M, selectedMetrics: C, selectedDimensions: v, filters: k, dialect: g, cubeName: E, availability: h, links: K }) { const [F, R] = b(!1), [$, j] = b(null), [W, O] = b("asc"), [I, ue] = b("table"), [S, Ne] = b(null), z = ee(null), ge = L( (x) => { x.preventDefault(); const D = z.current; if (!D) return; const B = x.clientY, X = S ?? D.offsetHeight * 0.333, ne = ($e) => { const Se = $e.clientY - B, Ae = Math.max( 80, Math.min(D.offsetHeight * 0.8, X + Se) ); Ne(Ae); }, be = () => { document.removeEventListener("mousemove", ne), document.removeEventListener("mouseup", be); }; document.addEventListener("mousemove", ne), document.addEventListener("mouseup", be); }, [S] ), ze = L(() => { l && (navigator.clipboard.writeText(l), R(!0), setTimeout(() => R(!1), 2e3)); }, [l]), H = de( () => { var x, D; return ((D = (x = d == null ? void 0 : d.results) == null ? void 0 : x[0]) == null ? void 0 : D.columns) || []; }, [d] ), Y = de(() => { var x, D; return ((D = (x = d == null ? void 0 : d.results) == null ? void 0 : x[0]) == null ? void 0 : D.rows) || []; }, [d]), oe = Y.length, ke = L( (x) => { $ === x ? O((D) => D === "asc" ? "desc" : "asc") : (j(x), O("asc")); }, [$] ), Ce = de(() => $ === null ? Y : [...Y].sort((x, D) => { const B = x[$], X = D[$]; if (B === null && X === null) return 0; if (B === null) return 1; if (X === null) return -1; let ne; return typeof B == "number" && typeof X == "number" ? ne = B - X : ne = String(B).localeCompare(String(X)), W === "asc" ? ne : -ne; }), [Y, $, W]), V = de( () => nn(H, Y), [H, Y] ), { chartData: Pe, pivotedByMetric: _e, groupValues: Le } = de(() => { if (!V || !V.xCol) return { chartData: [], pivotedByMetric: null, groupValues: null }; if (V.groupByCol) { const { pivotedByMetric: x, groupValues: D } = sn( Y, H, V.xCol, V.groupByCol, V.metricCols ); return { chartData: x[0].data, pivotedByMetric: x, groupValues: D }; } return { chartData: rn(H, Y, V.xCol), pivotedByMetric: null, groupValues: null }; }, [H, Y, V]), te = oe > 0; return Q(() => { !te && I === "chart" && ue("table"); }, [te, I]), /* @__PURE__ */ i("div", { className: "results-view", children: [ /* @__PURE__ */ i("div", { className: "results-header", children: [ /* @__PURE__ */ i("button", { className: "back-to-plan-btn", onClick: M, children: [ /* @__PURE__ */ t("span", { className: "back-arrow", children: "โ†" }), /* @__PURE__ */ t("span", { children: "Back to Plan" }) ] }), /* @__PURE__ */ t("div", { className: "results-summary", children: N ? /* @__PURE__ */ t("span", { className: "results-loading-text", children: "Running query..." }) : _ ? /* @__PURE__ */ t("span", { className: "results-error-text", children: "Query failed" }) : /* @__PURE__ */ i(Re, { children: [ /* @__PURE__ */ i("span", { className: "results-count", children: [ oe.toLocaleString(), " rows" ] }), w != null && /* @__PURE__ */ i("span", { className: "results-time", children: [ w.toFixed(2), "s" ] }) ] }) }) ] }), /* @__PURE__ */ i("div", { className: "results-panes", ref: z, children: [ /* @__PURE__ */ i( "div", { className: "sql-pane", style: S != null ? { flex: `0 0 ${S}px`, maxHeight: S } : void 0, children: [ /* @__PURE__ */ i("div", { className: "sql-pane-header", children: [ /* @__PURE__ */ t("span", { className: "sql-pane-title", children: "SQL Query" }), E && /* @__PURE__ */ i( "span", { className: "sql-pane-info", title: h ? `Querying materialized dataset ${[ h.catalog, h.schema_, h.table ].filter(Boolean).join(".")}, last refreshed for data through ${new Date( h.validThroughTs ).toLocaleDateString()}` : void 0, children: [ /* @__PURE__ */ i("span", { className: "info-materialized", children: [ /* @__PURE__ */ t("span", { style: { fontFamily: "sans-serif" }, children: "โšก" }), " Using materialized cube" ] }), (h == null ? void 0 : h.validThroughTs) && /* @__PURE__ */ i(Re, { children: [ " ยท Valid thru ", new Date(h.validThroughTs).toLocaleDateString() ] }) ] } ), /* @__PURE__ */ t( "button", { className: `copy-btn ${F ? "copied" : ""}`, onClick: ze, disabled: !l, children: F ? "โœ“ Copied" : "Copy" } ) ] }), /* @__PURE__ */ t("div", { className: "sql-pane-content", children: l ? /* @__PURE__ */ t( Wt, { language: "sql", style: Ut, wrapLongLines: !0, customStyle: { margin: 0, padding: "12px 16px", background: "#f8fafc", fontSize: "12px", height: "100%", overflow: "auto" }, children: l } ) : /* @__PURE__ */ t("div", { className: "sql-pane-empty", children: "Generating SQL..." }) }) ] } ), /* @__PURE__ */ t( "div", { className: "horizontal-resizer", onMouseDown: ge, title: "Drag to resize" } ), /* @__PURE__ */ t("div", { className: "results-pane", children: N ? /* @__PURE__ */ i("div", { className: "results-loading", children: [ /* @__PURE__ */ t("div", { className: "loading-spinner large" }), /* @__PURE__ */ t("span", { children: "Executing query..." }), /* @__PURE__ */ i("span", { className: "loading-hint", children: [ "Querying ", C.length, " metric(s) with", " ", v.length, " dimension(s)" ] }), K && K.length > 0 && /* @__PURE__ */ t("span", { className: "results-links", children: K.map((x, D) => /* @__PURE__ */ t( "a", { href: x, target: "_blank", rel: "noopener noreferrer", className: "results-link", children: "View query โ†—" }, D )) }) ] }) : _ ? /* @__PURE__ */ i("div", { className: "results-error", children: [ /* @__PURE__ */ t("div", { className: "error-icon", children: "โš " }), /* @__PURE__ */ t("h3", { children: "Query Failed" }), /* @__PURE__ */ t("p", { className: "error-message", children: _ }), /* @__PURE__ */ t( "button", { className: "action-btn action-btn-primary", onClick: M, children: "Back to Plan" } ) ] }) : /* @__PURE__ */ i("div", { className: "results-table-section", children: [ /* @__PURE__ */ i("div", { className: "results-tabs-bar", children: [ /* @__PURE__ */ i("div", { className: "results-tabs", children: [ /* @__PURE__ */ t( "button", { className: `results-tab ${I === "table" ? "active" : ""}`, onClick: () => ue("table"), children: "Table" } ), /* @__PURE__ */ t( "button", { className: `results-tab ${I === "chart" ? "active" : ""} ${te ? "" : "disabled"}`, onClick: () => te && ue("chart"), title: te ? void 0 : "No chartable data (need at least one numeric column)", children: "Chart" } ) ] }), /* @__PURE__ */ i("div", { className: "results-tabs-meta", children: [ /* @__PURE__ */ i("span", { className: "table-count", children: [ oe.toLocaleString(), " rows" ] }), k && k.length > 0 && /* @__PURE__ */ t("div", { className: "table-filters", children: k.map((x, D) => /* @__PURE__ */ t("span", { className: "filter-chip small", children: x }, D)) }) ] }) ] }), I === "table" ? /* @__PURE__ */ t("div", { className: "results-table-wrapper", children: oe === 0 ? /* @__PURE__ */ t("div", { className: "table-empty", children: /* @__PURE__ */ t("p", { children: "No results returned" }) }) : /* @__PURE__ */ i("table", { className: "results-table", children: [ /* @__PURE__ */ t("thead", { children: /* @__PURE__ */ t("tr", { children: H.map((x, D) => /* @__PURE__ */ i( "th", { title: x.semantic_name || x.name, onClick: () => ke(D), className: $ === D ? "sorted" : "", children: [ /* @__PURE__ */ i("span", { className: "col-header-content", children: [ x.name, /* @__PURE__ */ i("span", { className: "sort-arrows", children: [ /* @__PURE__ */ t( "span", { className: `sort-arrow up ${$ === D && W === "asc" ? "active" : ""}`, children: "โ–ฒ" } ), /* @__PURE__ */ t( "span", { className: `sort-arrow down ${$ === D && W === "desc" ? "active" : ""}`, children: "โ–ผ" } ) ] }) ] }), /* @__PURE__ */ t("span", { className: "col-type", children: x.type }) ] }, D )) }) }), /* @__PURE__ */ t("tbody", { children: Ce.map((x, D) => /* @__PURE__ */ t("tr", { children: x.map((B, X) => /* @__PURE__ */ t("td", { children: B === null ? /* @__PURE__ */ t("span", { className: "null-value", children: "NULL" }) : String(B) }, X)) }, D)) }) ] }) }) : /* @__PURE__ */ t("div", { className: "results-chart-wrapper", children: te ? /* @__PURE__ */ t( cn, { chartConfig: V, chartData: Pe, pivotedByMetric: _e, groupValues: Le, rows: Y, columns: H } ) : /* @__PURE__ */ t("div", { className: "chart-no-data", children: "No chartable data detected" }) }) ] }) }) ] }) ] }); } const un = Kt(() => import("./MetricFlowGraph-CgV3lxXj.js")); function vt(l) { return (l || []).map((d) => d.split(".").pop()).sort().join(","); } function vn() { var e, r, m, f, p; const l = jt(Ot).DataJunctionAPI, d = Bt(), N = qt(), [_, w] = b([]), [M, C] = b([]), [v, k] = b([]), [g, E] = b([]), [h, K] = b([]), [F, R] = b(null), $ = ee(!1), j = ee([]), W = ee(null), O = ee([]), [I, ue] = b(null), [S, Ne] = b(null), [z, ge] = b(null), [ze, H] = b(!1), [Y, oe] = b(!1), [ke, Ce] = b(null), [V, Pe] = b(!1), [_e, Le] = b(null), [te, x] = b(!1), [D, B] = b(null), [X, ne] = b(null), [be, $e] = b(null), [Se, Ae] = b([]), [ae, Te] = b([]), [we, Ke] = b(null), [Be, Fe] = b(null), [T, me] = b(null), [he, Me] = b(null), [ve, De] = b(null), We = ee(null), Ee = L( (s) => { s.preventDefault(); const n = We.current; if (!n) return; const a = s.clientX, o = he ?? n.offsetWidth * 0.2, c = (A) => { const q = A.clientX - a, P = Math.max( 200,