datajunction-ui
Version:
DataJunction UI
1,400 lines (1,399 loc) • 47.5 kB
JavaScript
var Fe = Object.defineProperty;
var Ae = (n, i, o) => i in n ? Fe(n, i, { enumerable: !0, configurable: !0, writable: !0, value: o }) : n[i] = o;
var fe = (n, i, o) => Ae(n, typeof i != "symbol" ? i + "" : i, o);
import { jsxs as y, jsx as e, Fragment as K } from "react/jsx-runtime";
import { cz as W, bp as ge, o as q, q as U, W as ye, a9 as Re, aj as qe, b4 as Oe, j as _e, cg as Le, n as je, u as $e, ci as C, D as P, bo as Ue, t as Q, cv as Pe, cy as ze, b as Je, N as be, s as Ve, r as Be, aH as Qe, z as He } from "./index-n79cxNwC.js";
import T, { useContext as z, useState as A, useEffect as $, useMemo as Ke } from "react";
import { useNavigate as We, useParams as Ze } from "react-router-dom";
import { F as Ge } from "./FullNameField-fHyJsZmF.js";
const Ye = ({ djClient: n, value: i }) => {
const [o, c] = T.useState({}), [d, s] = T.useState([]), l = W(), r = l.values.upstream_node, m = T.useMemo(() => ge.sql({ schema: o }), [o]);
T.useEffect(() => {
async function v() {
try {
const N = await n.metrics();
s(N || []);
} catch (N) {
console.error("Failed to load metrics for autocomplete:", N);
}
}
v();
}, [n]), T.useEffect(() => {
let v = !1;
async function N() {
var D;
const w = {};
if (r && r.trim() !== "")
try {
const R = await n.node(r);
(D = R == null ? void 0 : R.columns) == null || D.forEach((J) => {
w[J.name] = [];
});
} catch (R) {
console.error("Failed to load upstream node columns:", R);
}
d.forEach((R) => {
w[R] = [];
}), v || c(w);
}
return N(), () => {
v = !0;
};
}, [n, r, d]);
const h = (v) => {
l.setFieldValue("aggregate_expression", v);
}, f = !r || r.trim() === "";
return /* @__PURE__ */ y("div", { className: "QueryInput MetricQueryInput NodeCreationInput", children: [
/* @__PURE__ */ e(q, { name: "query", component: "span" }),
/* @__PURE__ */ e("label", { htmlFor: "Query", children: f ? "Derived Metric Expression *" : "Aggregate Expression *" }),
/* @__PURE__ */ e(
"p",
{
className: "field-help-text",
style: { fontSize: "0.85em", color: "#666", marginBottom: "8px" },
children: f ? "Reference other metrics using their full names (e.g., namespace.metric_name / namespace.other_metric)" : "Use aggregate functions on columns from the upstream node (e.g., SUM(column_name))"
}
),
/* @__PURE__ */ e(
U,
{
type: "textarea",
style: { display: "none" },
as: "textarea",
name: "aggregate_expression",
id: "Query"
}
),
/* @__PURE__ */ e("div", { role: "button", tabIndex: 0, className: "relative flex bg-[#282a36]", children: /* @__PURE__ */ e(
ye,
{
id: "aggregate_expression",
name: "aggregate_expression",
extensions: [m],
value: i,
options: {
theme: "default",
lineNumbers: !0
},
width: "100%",
height: "100px",
style: {
margin: "0 0 23px 0",
flex: 1,
fontSize: "110%",
textAlign: "left"
},
onChange: (v, N) => {
h(v);
}
}
) })
] });
}, Xe = /`?([a-zA-Z_][a-zA-Z0-9_]*)`?\.`?([a-zA-Z_][a-zA-Z0-9_]*)`?\.`?([a-zA-Z_][a-zA-Z0-9_]*)`?/;
function et(n, i) {
const o = new RegExp(Xe.source, "g"), c = new Set(i.map((r) => r.toLowerCase())), d = /* @__PURE__ */ new Set(), s = [];
let l;
for (; (l = o.exec(n)) !== null; ) {
const [, r, m, h] = l;
if (!c.has(r.toLowerCase())) continue;
const f = `${r}.${m}.${h}`;
d.has(f) || (d.add(f), s.push([r, m, h]));
}
return s;
}
const oe = /`?[a-zA-Z_][a-zA-Z0-9_]*`?(?:\.`?[a-zA-Z_][a-zA-Z0-9_]*`?)+/g;
function Ie(n) {
return n.replace(/`/g, "");
}
const he = Re.define();
function tt(n) {
switch (n) {
case "registering":
return "dj-node-chip--registering";
case "valid":
return "dj-node-chip--valid";
case "warning":
return "dj-node-chip--warning";
case "invalid":
return "dj-node-chip--invalid";
default:
return "dj-node-chip--unknown";
}
}
function Ce(n, i, o) {
const c = new Set((o() || []).map((l) => l.toLowerCase())), d = n.state.doc, s = [];
for (const { from: l, to: r } of n.visibleRanges) {
const m = d.sliceString(l, r);
oe.lastIndex = 0;
let h;
for (; (h = oe.exec(m)) !== null; ) {
const f = h[0], _ = Ie(f), b = i(_), v = _.split(".");
let N;
if (b != null && b.refType)
N = b.refType;
else if (v.length === 3 && c.has(v[0].toLowerCase()))
N = "source";
else
continue;
const w = `dj-node-chip dj-node-chip--${N} ${tt(
b == null ? void 0 : b.kind
)}`, D = l + h.index, R = D + f.length;
s.push({
from: D,
to: R,
deco: _e.mark({
class: w,
attributes: b != null && b.message ? { title: b.message } : void 0
})
});
}
}
return s.sort((l, r) => l.from - r.from || l.to - r.to), _e.set(s.map((l) => l.deco.range(l.from, l.to)));
}
function nt({ getStatus: n, getKnownCatalogs: i }) {
return qe.fromClass(
class {
constructor(o) {
this.decorations = Ce(o, n, i);
}
update(o) {
const c = o.transactions.some(
(d) => d.effects.some((s) => s.is(he))
);
(o.docChanged || o.viewportChanged || c) && (this.decorations = Ce(
o.view,
n,
i
));
}
},
{ decorations: (o) => o.decorations }
);
}
function at(n, i) {
const o = n.state.doc.lineAt(i);
oe.lastIndex = 0;
let c;
for (; (c = oe.exec(o.text)) !== null; ) {
const d = o.from + c.index, s = d + c[0].length;
if (i >= d && i <= s)
return { key: Ie(c[0]), from: d, to: s };
}
return null;
}
function xe(n, i) {
var h;
const o = document.createElement("div");
o.className = "dj-node-tooltip";
const c = n.node || {}, d = document.createElement("div");
d.className = "dj-node-tooltip__header";
const s = document.createElement("span");
s.className = `dj-node-chip dj-node-chip--${n.refType || "node"}`, s.textContent = n.refType || "node", d.appendChild(s);
const l = document.createElement("span");
if (l.className = "dj-node-tooltip__name", l.textContent = c.name || i, d.appendChild(l), o.appendChild(d), n.kind === "invalid") {
const f = document.createElement("div");
return f.className = "dj-node-tooltip__note", f.textContent = "Not found in DJ.", o.appendChild(f), o;
}
if (n.kind === "registering") {
const f = document.createElement("div");
return f.className = "dj-node-tooltip__note", f.textContent = "Registering…", o.appendChild(f), o;
}
const r = document.createElement("div");
r.className = "dj-node-tooltip__grid";
const m = (f, _) => {
if (_ == null || _ === "") return;
const b = document.createElement("div");
b.className = "dj-node-tooltip__cell";
const v = document.createElement("span");
v.className = "dj-node-tooltip__label", v.textContent = f;
const N = document.createElement("span");
N.className = "dj-node-tooltip__value", N.textContent = _, b.appendChild(v), b.appendChild(N), r.appendChild(b);
};
if (c.status && m("Status", c.status), c.version && m("Version", c.version), c.mode && m("Mode", c.mode), Array.isArray(c.columns) && m("Columns", String(c.columns.length)), r.childElementCount > 0 && o.appendChild(r), c.description) {
const f = document.createElement("div");
f.className = "dj-node-tooltip__description";
const _ = c.description;
f.textContent = _.length > 280 ? _.slice(0, 277) + "…" : _, o.appendChild(f);
}
if (Array.isArray(c.columns) && c.columns.length > 0) {
const f = document.createElement("div");
f.className = "dj-node-tooltip__columns";
for (const _ of c.columns) {
const b = document.createElement("div");
b.className = "dj-node-tooltip__col-row";
const v = document.createElement("span");
if (v.className = "dj-node-tooltip__col-name", v.textContent = _.name, b.appendChild(v), _.dimension || _.dimension_column) {
const w = document.createElement("span");
w.className = "dj-node-tooltip__col-tag", w.textContent = "dim", w.title = (h = _.dimension) != null && h.name ? `Links to ${_.dimension.name}` : "Linked dimension", b.appendChild(w);
}
const N = document.createElement("span");
N.className = "dj-node-tooltip__col-type", N.textContent = (_.type || "").split(`
`)[0], N.title = _.type || "", b.appendChild(N), f.appendChild(b);
}
o.appendChild(f);
}
if (c.name) {
const f = document.createElement("div");
f.className = "dj-node-tooltip__footer";
const _ = document.createElement("a");
_.href = `/nodes/${encodeURIComponent(c.name)}`, _.target = "_blank", _.rel = "noreferrer noopener", _.textContent = "Open node →", f.appendChild(_), o.appendChild(f);
}
return o;
}
const ve = /* @__PURE__ */ new Map();
function rt({
getStatus: n,
getKnownCatalogs: i,
fetchNodeDetails: o
}) {
return Oe(
(c, d) => {
const s = at(c, d);
if (!s) return null;
const l = n(s.key);
if (!l) {
const r = new Set(
(i() || []).map((h) => h.toLowerCase())
), m = s.key.split(".");
if (m.length !== 3 || !r.has(m[0].toLowerCase()))
return null;
}
return {
pos: s.from,
end: s.to,
above: !0,
create: () => {
const r = l || { refType: "source" }, m = xe(r, s.key);
if (o && r.kind !== "invalid" && r.kind !== "registering") {
const h = ve.get(s.key), f = h || o(s.key);
h || ve.set(s.key, f), Promise.resolve(f).then((_) => {
if (!_) return;
const b = xe(
{
...r,
node: { ...r.node || {}, ..._ }
},
s.key
);
m.parentNode ? m.parentNode.replaceChild(b, m) : m.replaceChildren(...b.childNodes);
}).catch(() => {
});
}
return { dom: m };
}
};
},
{ hoverTime: 150 }
);
}
const M = {
bg: "#fbfbfd",
surface: "#f6f6f9",
gutter: "#9aa2b1",
selection: "#dbe2ec",
caret: "#3b3f46",
text: "#27303d",
muted: "#8b94a5",
keyword: "#6b3aa0",
// matches cube/dimension violet family
string: "#0e7c5a",
// forest green
number: "#a96621",
// warm amber (same as dimension chip)
operator: "#475569",
functionName: "#0063b4",
// transform blue
punctuation: "#5c6573",
comment: "#9ca3af"
}, ot = je.theme(
{
"&": {
color: M.text,
backgroundColor: M.bg,
fontSize: "13px"
},
".cm-content": {
caretColor: M.caret,
fontFamily: '"JetBrains Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", monospace'
},
".cm-cursor, .cm-dropCursor": { borderLeftColor: M.caret },
"&.cm-focused .cm-selectionBackground, ::selection, .cm-selectionBackground": {
backgroundColor: M.selection
},
".cm-activeLine": {
backgroundColor: "rgba(99, 102, 241, 0.05)"
},
".cm-gutters": {
backgroundColor: M.surface,
color: M.gutter,
border: "none"
},
".cm-activeLineGutter": {
backgroundColor: "rgba(99, 102, 241, 0.08)",
color: M.text
},
".cm-lineNumbers .cm-gutterElement": {
padding: "0 8px 0 6px"
},
".cm-foldGutter .cm-gutterElement": {
color: M.muted
},
".cm-tooltip": {
background: "#ffffff",
border: "1px solid #e2e8f0",
borderRadius: "6px",
boxShadow: "0 4px 12px rgba(15, 23, 42, 0.06)"
},
".cm-tooltip-autocomplete > ul > li[aria-selected]": {
background: "#eef2ff",
color: M.text
},
".cm-matchingBracket, .cm-nonmatchingBracket": {
backgroundColor: "#fef3c7",
outline: "none"
},
".cm-panels": {
backgroundColor: M.surface,
color: M.text
}
},
{ dark: !1 }
), st = $e.define([
{
tag: [C.keyword, C.modifier, C.controlKeyword, C.operatorKeyword],
color: M.keyword,
fontWeight: "500"
},
{ tag: [C.string, C.special(C.string)], color: M.string },
{ tag: [C.number, C.bool, C.null], color: M.number },
{
tag: [C.function(C.variableName), C.function(C.propertyName)],
color: M.functionName
},
{
tag: [C.operator, C.compareOperator, C.arithmeticOperator, C.logicOperator],
color: M.operator
},
{
tag: [C.punctuation, C.paren, C.brace, C.bracket, C.separator],
color: M.punctuation
},
{
tag: [C.variableName, C.propertyName, C.attributeName],
color: M.text
},
{ tag: [C.typeName, C.className], color: M.functionName },
{
tag: [C.comment, C.lineComment, C.blockComment],
color: M.comment,
fontStyle: "italic"
},
{ tag: C.invalid, color: "#cc2222" }
]), it = [
ot,
Le(st)
], we = {
INVALID_SQL_QUERY: "Invalid SQL",
TYPE_INFERENCE: "Type inference",
INVALID_COLUMN: "Invalid column",
INVALID_ARGUMENTS_TO_FUNCTION: "Invalid function arguments",
MISSING_COLUMNS: "Missing columns",
UNKNOWN_NODE: "Unknown node",
MISSING_PARENT: "Missing parent",
NODE_TYPE_ERROR: "Node type",
NOT_IMPLEMENTED_ERROR: "Not implemented"
}, ct = (n) => {
if (n == null || n === "") return "Error";
const i = String(n);
return we[i] ? we[i] : i.toLowerCase().split("_").map((o) => o ? o[0].toUpperCase() + o.slice(1) : "").join(" ");
}, lt = ({ djClient: n, value: i }) => {
const [o, c] = T.useState({}), d = W(), s = T.useMemo(() => ge.sql({ schema: o }), [o]), l = T.useRef(null), r = T.useRef(null), m = T.useRef(/* @__PURE__ */ new Set()), h = T.useRef([]), [f, _] = T.useState([]), [b, v] = T.useState({}), N = T.useRef(b);
T.useEffect(() => {
N.current = b;
}, [b]);
const w = T.useRef(null), D = T.useCallback((x, F) => {
N.current = { ...N.current, [x]: F }, v(N.current), w.current && w.current.dispatch({ effects: he.of(null) });
}, []);
T.useEffect(() => {
typeof n.catalogs == "function" && Promise.resolve(n.catalogs()).then((x) => {
h.current = (x || []).map(
(F) => F.name.toLowerCase()
);
}).catch(() => {
});
}, [n]);
const R = async (x) => {
const F = x.matchBefore(/[\.\w]*/), t = await n.nodes(F.text);
c((a) => {
const g = { ...a };
let p = !1;
for (const I of t)
g[I] === void 0 && (g[I] = [], p = !0);
return p ? g : a;
});
}, J = (x) => {
d.setFieldValue("query", x);
}, se = async (x, F) => {
for (const t of Object.keys(o))
if (x.includes(t) && o[t].length === 0) {
const a = await n.node(t), g = ((a == null ? void 0 : a.columns) || []).map((p) => p.name);
c((p) => ({ ...p, [t]: g }));
}
h.current.length > 0 && (clearTimeout(l.current), l.current = setTimeout(
() => ce(x),
600
)), clearTimeout(r.current), r.current = setTimeout(() => ie(x), 700);
}, ie = async (x) => {
if (typeof n.validateNode != "function") return;
const F = d.values || {};
if (!F.type) return;
let t;
try {
t = await n.validateNode(
F.type,
F.name || "__draft__",
F.display_name || "Draft",
F.description || "",
x
);
} catch {
return;
}
const a = (t == null ? void 0 : t.json) || {}, g = a.dependencies || [], p = a.missing_parents || [];
_(Array.isArray(a.errors) ? a.errors : []);
const I = { ...N.current };
for (const E of g) {
const k = typeof E == "string" ? E : E == null ? void 0 : E.name;
if (!k) continue;
const O = typeof E == "object" ? E.type : void 0, j = typeof E == "object" ? E.status : void 0, u = j && j !== "valid" ? "warning" : "valid";
I[k] = {
kind: u,
refType: O || "node",
node: typeof E == "object" ? E : void 0,
message: u === "warning" ? `Node \`${k}\` is ${j}` : void 0
};
}
for (const E of p) {
const k = typeof E == "string" ? E : E == null ? void 0 : E.name;
k && (I[k] = {
kind: "invalid",
refType: "node",
message: `Node \`${k}\` not found`
});
}
N.current = I, v(I), w.current && w.current.dispatch({ effects: he.of(null) });
}, ce = async (x) => {
const F = et(x, h.current);
for (const [t, a, g] of F) {
const p = `${t}.${a}.${g}`;
if (m.current.has(p)) continue;
if (o[p] !== void 0) {
m.current.add(p), D(p, { kind: "valid" });
continue;
}
m.current.add(p), D(p, { kind: "registering" });
let I;
try {
I = await n.registerTable(
t,
a,
g,
""
);
} catch (O) {
m.current.delete(p), D(p, {
kind: "invalid",
message: (O == null ? void 0 : O.message) || "Failed to register table"
});
continue;
}
const { status: E, json: k } = I;
if (E === 200 || E === 201) {
const O = (k == null ? void 0 : k.name) || p, j = ((k == null ? void 0 : k.columns) || []).map((u) => u.name);
c((u) => {
const L = { ...u, [O]: j };
return g && g !== O && (L[g] = j), L;
}), D(p, { kind: "valid" });
} else E === 409 ? D(p, { kind: "valid" }) : (m.current.delete(p), D(p, {
kind: "invalid",
message: (k == null ? void 0 : k.message) || `Registration failed (HTTP ${E})`
}));
}
}, le = T.useMemo(
() => nt({
getStatus: (x) => N.current[x],
getKnownCatalogs: () => h.current
}),
[]
), de = T.useMemo(
() => rt({
getStatus: (x) => N.current[x],
getKnownCatalogs: () => h.current,
fetchNodeDetails: (x) => n.node(x).catch(() => null)
}),
[n]
);
return /* @__PURE__ */ y("div", { className: "QueryInput NodeCreationInput", children: [
/* @__PURE__ */ e(q, { name: "query", component: "span" }),
/* @__PURE__ */ e("label", { htmlFor: "Query", children: "Query *" }),
/* @__PURE__ */ e(
U,
{
type: "textarea",
style: { display: "none" },
as: "textarea",
name: "query",
id: "Query"
}
),
f.length > 0 && /* @__PURE__ */ e("div", { className: "query-validation-errors", role: "alert", children: f.map((x, F) => /* @__PURE__ */ y("div", { className: "query-validation-error", children: [
/* @__PURE__ */ e("span", { className: "query-validation-error-code", children: ct(x.code) }),
/* @__PURE__ */ e("span", { className: "query-validation-error-message", children: x.message })
] }, F)) }),
/* @__PURE__ */ e("div", { role: "button", tabIndex: 0, className: "relative flex bg-[#282a36]", children: /* @__PURE__ */ e(
ye,
{
id: "query",
name: "query",
extensions: [
s,
s.language.data.of({
autocomplete: R
}),
...it,
le,
de
],
onCreateEditor: (x) => {
w.current = x;
},
value: i,
placeholder: `SELECT
primary_key,
measure1,
measure2,
foreign_key_for_dimension1,
foreign_key_for_dimension2
FROM source.source_node
WHERE ...`,
options: {
theme: "default",
lineNumbers: !0
},
width: "100%",
height: "400px",
style: {
margin: "0 0 23px 0",
flex: 1,
fontSize: "110%",
textAlign: "left"
},
onChange: (x, F) => {
J(x), se(x);
}
}
) })
] });
}, dt = () => {
const n = z(P).DataJunctionAPI, [i, o] = A([]), [c, d] = A([]);
return $(() => {
(async () => {
const l = await n.listMetricMetadata();
d(l.directions), o(l.units);
})().catch(console.error);
}, [n]), /* @__PURE__ */ y("div", { className: "MetricMetadataFields node-row", children: [
/* @__PURE__ */ y("div", { className: "NodeCreationInput NodeModeInput", children: [
/* @__PURE__ */ e(q, { name: "metric_direction", component: "span" }),
/* @__PURE__ */ e("label", { htmlFor: "MetricDirection", children: "Direction" }),
/* @__PURE__ */ y(U, { as: "select", name: "metric_direction", id: "MetricDirection", children: [
/* @__PURE__ */ e("option", { value: "" }),
c.map((s) => /* @__PURE__ */ e("option", { value: s, children: Ue(s) }, s))
] })
] }),
/* @__PURE__ */ y("div", { className: "NodeCreationInput NodeModeInput", children: [
/* @__PURE__ */ e(q, { name: "metric_unit", component: "span" }),
/* @__PURE__ */ e("label", { htmlFor: "MetricUnit", children: "Unit" }),
/* @__PURE__ */ y(U, { as: "select", name: "metric_unit", id: "MetricUnit", children: [
/* @__PURE__ */ e("option", { value: "" }),
i.map((s) => /* @__PURE__ */ e("option", { value: s.name, children: s.label }, s.name))
] })
] }),
/* @__PURE__ */ y("div", { className: "NodeCreationInput NodeModeInput", children: [
/* @__PURE__ */ e(q, { name: "significant_digits", component: "span" }),
/* @__PURE__ */ e("label", { htmlFor: "SignificantDigits", children: "Significant Digits" }),
/* @__PURE__ */ y(U, { as: "select", name: "significant_digits", id: "SignificantDigits", children: [
/* @__PURE__ */ e("option", { value: "" }),
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((s) => /* @__PURE__ */ e("option", { value: s, children: s }, s))
] })
] })
] });
}, Ee = ({ defaultValue: n }) => {
const i = z(P).DataJunctionAPI, [o, c] = A([]);
return $(() => {
async function d() {
const s = await i.nodesWithType("source"), l = await i.nodesWithType("transform"), r = await i.nodesWithType("dimension"), m = s.concat(l).concat(r);
c(
m.map((h) => ({
value: h,
label: h
}))
);
}
d();
}, [i]), /* @__PURE__ */ y("div", { className: "NodeCreationInput", children: [
/* @__PURE__ */ e(q, { name: "upstream_node", component: "span" }),
/* @__PURE__ */ e("label", { htmlFor: "upstream_node", children: "Upstream Node" }),
/* @__PURE__ */ y(
"p",
{
className: "field-help-text",
style: { fontSize: "0.85em", color: "#666", marginBottom: "8px" },
children: [
"Select a source, transform, or dimension for regular metrics. Leave empty for ",
/* @__PURE__ */ e("strong", { children: "derived metrics" }),
" that reference other metrics (e.g., ",
/* @__PURE__ */ e("code", { children: "namespace.metric_a / namespace.metric_b" }),
")."
]
}
),
/* @__PURE__ */ e("span", { "data-testid": "select-upstream-node", children: /* @__PURE__ */ e(
Q,
{
className: "",
defaultValue: n,
selectOptions: o,
formikFieldName: "upstream_node",
placeholder: "Select Upstream Node (optional for derived metrics)",
isMulti: !1,
isClearable: !0
}
) })
] });
}, ke = ({ defaultValue: n }) => {
const i = z(P).DataJunctionAPI, { currentUser: o } = Pe(), [, c, d] = ze("owners"), [s, l] = A([]);
return $(() => {
async function r() {
const m = await i.users();
l(
m.map((h) => ({
value: h.username,
label: h.username
}))
);
}
r();
}, [i]), $(() => {
c.value && c.value.length > 0 || (n && n.length > 0 ? d.setValue(n.map((r) => r.value)) : o && d.setValue([o.username]));
}, [n, o]), n || o ? /* @__PURE__ */ y("div", { className: "NodeCreationInput", children: [
/* @__PURE__ */ e(q, { name: "owners", component: "span" }),
/* @__PURE__ */ e("label", { htmlFor: "Owners", children: "Owners" }),
/* @__PURE__ */ e("span", { "data-testid": "select-owner", children: /* @__PURE__ */ e(
Q,
{
className: "",
selectOptions: s,
formikFieldName: "owners",
placeholder: "Select Owners",
isMulti: !0
}
) })
] }) : "";
}, Se = ({ defaultValue: n }) => {
const i = z(P).DataJunctionAPI, [o, c] = A([]);
return $(() => {
(async () => {
const s = await i.listTags();
c(
s.map((l) => ({
value: l.name,
label: l.display_name
}))
);
})().catch(console.error);
}, [i]), /* @__PURE__ */ y("div", { className: "TagsInput NodeCreationInput", children: [
/* @__PURE__ */ e(q, { name: "tags", component: "span" }),
/* @__PURE__ */ e("label", { htmlFor: "tags", children: "Tags" }),
/* @__PURE__ */ e("span", { "data-testid": "select-tags", children: /* @__PURE__ */ e(
Q,
{
isMulti: !0,
selectOptions: o,
formikFieldName: "tags",
className: "",
placeholder: "Choose Tags",
defaultValue: n
}
) })
] });
}, mt = ({ initialNamespace: n }) => {
const i = z(P).DataJunctionAPI, [o, c] = A([]);
return $(() => {
(async () => {
const s = await i.namespaces();
c(
s.map((l) => ({
value: l.namespace,
label: l.namespace
}))
);
})().catch(console.error);
}, [i]), /* @__PURE__ */ y("div", { className: "NamespaceInput", children: [
/* @__PURE__ */ e(q, { name: "namespace", component: "span" }),
/* @__PURE__ */ e("label", { htmlFor: "namespace", children: "Namespace *" }),
/* @__PURE__ */ e(
Q,
{
className: "",
selectOptions: o,
formikFieldName: "namespace",
placeholder: "Choose Namespace",
defaultValue: {
value: n,
label: n
}
}
)
] });
}, ut = ({ message: n }) => /* @__PURE__ */ y("div", { className: "message alert", children: [
/* @__PURE__ */ e(Je, {}),
n
] }), pt = () => /* @__PURE__ */ y("div", { className: "DisplayNameInput NodeCreationInput", children: [
/* @__PURE__ */ e(q, { name: "display_name", component: "span" }),
/* @__PURE__ */ e("label", { htmlFor: "displayName", children: "Display Name *" }),
/* @__PURE__ */ e(
U,
{
type: "text",
name: "display_name",
id: "displayName",
placeholder: "Human readable display name"
}
)
] }), ft = () => /* @__PURE__ */ y("div", { className: "DescriptionInput NodeCreationInput", children: [
/* @__PURE__ */ e(q, { name: "description", component: "span" }),
/* @__PURE__ */ e("label", { htmlFor: "Description", children: "Description" }),
/* @__PURE__ */ e(
U,
{
type: "textarea",
as: "textarea",
name: "description",
id: "Description",
placeholder: "Describe your node"
}
)
] }), ht = () => /* @__PURE__ */ y("div", { className: "NodeModeInput NodeCreationInput", children: [
/* @__PURE__ */ e(q, { name: "mode", component: "span" }),
/* @__PURE__ */ e("label", { htmlFor: "Mode", children: "Mode" }),
/* @__PURE__ */ y(U, { as: "select", name: "mode", id: "Mode", children: [
/* @__PURE__ */ e("option", { value: "draft", children: "Draft" }),
/* @__PURE__ */ e("option", { value: "published", children: "Published" })
] })
] }), Me = ({
defaultValue: n,
style: i,
className: o = ""
}) => {
const c = z(P).DataJunctionAPI, { values: d } = W(), [s, l] = A([]);
return $(() => {
(async () => {
if (d.upstream_node) {
const m = await c.node(d.upstream_node);
l(
m.columns.map((h) => ({
value: h.name,
label: h.name
}))
);
}
})().catch(console.error);
}, [c, d.upstream_node]), /* @__PURE__ */ y("div", { className: "RequiredDimensionsInput CubeCreationInput", children: [
/* @__PURE__ */ e(q, { name: "required_dimensions", component: "span" }),
/* @__PURE__ */ e("label", { htmlFor: "requiredDimensions", children: "Required Dimensions" }),
/* @__PURE__ */ e(
Q,
{
className: o,
defaultValue: n,
selectOptions: s,
formikFieldName: "required_dimensions",
placeholder: "Choose Required Dimensions",
styles: i,
isMulti: !0
}
)
] });
}, De = ({
defaultValue: n,
fieldName: i,
label: o,
labelStyle: c = {},
isMulti: d = !1,
isClearable: s = !0
}) => {
const l = z(P).DataJunctionAPI, { values: r } = W(), [m, h] = A([]), [f, _] = A(null), b = Ke(() => m && m.length > 0 ? m : [], [m]), v = async () => {
try {
const { status: N, json: w } = await l.validateNode(
r.type,
r.name,
r.display_name,
r.description,
r.query
);
if (w != null && w.columns)
h(
w.columns.map((D) => ({ value: D.name, label: D.name }))
), _(null);
else {
const D = Array.isArray(w == null ? void 0 : w.errors) ? w.errors.map((R) => R.message).filter(Boolean).join(`
`) : "";
_(D || (w == null ? void 0 : w.message) || null), h([]);
}
} catch (N) {
console.error("Error fetching columns:", N), _(
N.message || "Failed to validate query. Please check your query syntax."
), h([]);
}
};
return $(() => {
v();
}, [r.type, r.name, r.query]), /* @__PURE__ */ y("div", { className: "CubeCreationInput", children: [
/* @__PURE__ */ e(q, { name: i, component: "span" }),
/* @__PURE__ */ e("label", { htmlFor: i, style: c, children: o }),
f && /* @__PURE__ */ y(
"div",
{
className: "validation-error",
style: {
color: "#d32f2f",
fontSize: "0.875rem",
marginBottom: "8px",
padding: "8px",
backgroundColor: "#ffebee",
borderRadius: "4px",
border: "1px solid #ef9a9a"
},
children: [
/* @__PURE__ */ e("strong", { children: "Validation Error:" }),
" ",
f
]
}
),
/* @__PURE__ */ e("span", { "data-testid": `select-${i}`, children: /* @__PURE__ */ e(
Q,
{
className: "",
defaultValue: d ? n.map((N) => ({
value: N,
label: N
})) : n ? { value: n, label: n } : null,
selectOptions: b,
formikFieldName: i,
onFocus: (N) => v(),
isMulti: d,
isClearable: s
}
) })
] });
}, gt = ({ value: n }) => {
const i = W(), o = ge.json(), [c, d] = A(!1);
$(() => {
if (!n || n === "") {
d(!1);
return;
}
const r = typeof n == "string" ? n : JSON.stringify(n, null, 2);
try {
JSON.parse(r), d(!1);
} catch {
d(!0);
}
}, [n]);
const s = (r) => r == null ? "" : typeof r == "string" ? r : JSON.stringify(r, null, 2), l = (r) => {
if (i.setFieldValue("custom_metadata", r), i.setFieldTouched("custom_metadata", !0), !r || r.trim() === "")
d(!1);
else
try {
JSON.parse(r), d(!1);
} catch {
d(!0);
}
};
return /* @__PURE__ */ e("div", { className: "QueryInput NodeCreationInput", children: /* @__PURE__ */ y("details", { children: [
/* @__PURE__ */ e("summary", { style: { cursor: "pointer" }, children: /* @__PURE__ */ e(
"label",
{
style: {
paddingLeft: "3px",
display: "inline-block",
pointerEvents: "none"
},
children: "Custom Metadata (JSON)"
}
) }),
/* @__PURE__ */ e(q, { name: "custom_metadata", component: "span" }),
/* @__PURE__ */ e(
U,
{
type: "textarea",
style: { display: "none" },
as: "textarea",
name: "custom_metadata",
id: "CustomMetadata",
validate: (r) => {
if (!(!r || r.trim() === ""))
try {
const m = JSON.parse(r);
if (typeof m == "object" && m !== null && !Array.isArray(m)) {
const h = Object.keys(m), f = r.match(/"([^"]+)"\s*:/g);
if (f && f.length > h.length)
return "Duplicate keys detected";
}
return;
} catch {
return "Invalid JSON format";
}
}
}
),
/* @__PURE__ */ e(
"div",
{
role: "button",
tabIndex: 0,
className: `relative flex ${c ? "bg-red-900/20" : "bg-[#282a36]"}`,
style: {
border: c ? "2px solid #ef4444" : "none",
borderRadius: "4px",
boxShadow: c ? "0 0 0 1px rgba(239, 68, 68, 0.3)" : "none"
},
children: /* @__PURE__ */ e(
ye,
{
id: "custom_metadata",
name: "custom_metadata",
extensions: [o],
value: s(n),
placeholder: `{
"key": "value"
}`,
options: {
theme: "default",
lineNumbers: !0
},
width: "100%",
height: "200px",
style: {
margin: "0 0 23px 0",
flex: 1,
fontSize: "110%",
textAlign: "left"
},
onChange: (r, m) => {
l(r);
}
}
)
}
)
] }) });
}, B = class B {
constructor(i) {
this.name = i;
}
};
fe(B, "Add", new B("add")), fe(B, "Edit", new B("edit"));
let S = B;
function vt({ extensions: n = {} }) {
const i = z(P).DataJunctionAPI, o = We();
let { nodeType: c, initialNamespace: d, name: s } = Ze();
const l = s !== void 0 ? S.Edit : S.Add, [r, m] = A(void 0);
$(() => {
l === S.Edit && r === !0 && o(`/nodes/${s}`, { replace: !0 });
}, [l, r, s, o]);
const h = d || (s ? s.substring(0, s.lastIndexOf(".")) : ""), f = {
name: l === S.Edit ? s : "",
namespace: l === S.Add ? d : "",
display_name: "",
query: "",
type: c,
description: "",
primary_key: "",
mode: "published",
owners: [],
custom_metadata: ""
}, _ = (t) => {
var g;
const a = {};
return l === S.Add && !((g = t.display_name) != null && g.trim()) && (a.display_name = "Required"), (!t.name || t.name.endsWith(".") || t.name.startsWith(".") || t.name.includes("..")) && (a.name = "Required"), t.type !== "metric" && !t.query && (a.query = "Required"), a;
}, v = [async (t, { setSubmitting: a, setStatus: g }) => {
l === S.Add ? await se(t, g).then((p) => {
window.scrollTo({ top: 0, left: 0, behavior: "smooth" }), a(!1);
}) : await ie(t, g).then((p) => {
window.scrollTo({ top: 0, left: 0, behavior: "smooth" }), a(!1);
});
}], N = l === S.Add ? /* @__PURE__ */ y("h2", { children: [
"Create",
" ",
/* @__PURE__ */ e("span", { className: `node_type__${c} node_type_creation_heading`, children: c })
] }) : /* @__PURE__ */ e("h2", { children: "Edit" }), w = (t) => /* @__PURE__ */ y(K, { children: [
/* @__PURE__ */ y("div", { className: "NodeNameInput NodeCreationInput", children: [
/* @__PURE__ */ e("label", { children: "Name" }),
" ",
s
] }),
/* @__PURE__ */ y("div", { className: "NodeNameInput NodeCreationInput", children: [
/* @__PURE__ */ e("label", { children: "Type" }),
" ",
t.type
] })
] }), D = (t) => t.map((a) => a.trim()), R = (t) => {
if (!t || t.trim() === "")
return null;
try {
return JSON.parse(t);
} catch {
return null;
}
}, J = (t, a) => a && a.trim() !== "" ? `SELECT ${t}
FROM ${a}` : `SELECT ${t}`, se = async (t, a) => {
const { status: g, json: p } = await i.createNode(
c,
t.name,
t.display_name,
t.description,
t.type === "metric" ? J(t.aggregate_expression, t.upstream_node) : t.query,
t.mode,
t.namespace,
t.primary_key ? D(t.primary_key) : null,
t.metric_direction,
t.metric_unit,
// Only send required_dimensions if it's set (for metrics)
t.required_dimensions && Array.isArray(t.required_dimensions) ? t.required_dimensions : void 0,
R(t.custom_metadata)
);
g === 200 || g === 201 ? (t.tags && await i.tagsNode(t.name, t.tags), a({
success: /* @__PURE__ */ y(K, { children: [
"Successfully created ",
p.type,
" node",
" ",
/* @__PURE__ */ e("a", { href: `/nodes/${p.name}`, children: p.name }),
"!"
] })
})) : a({
failure: `${p.message}`
});
}, ie = async (t, a) => {
const { status: g, json: p } = await i.patchNode(
t.name,
t.display_name,
t.description,
t.type === "metric" ? J(t.aggregate_expression, t.upstream_node) : t.query,
t.mode,
t.primary_key ? D(t.primary_key) : null,
t.metric_direction,
t.metric_unit,
t.significant_digits,
// Only send required_dimensions if it's set (for metrics)
t.required_dimensions && Array.isArray(t.required_dimensions) ? t.required_dimensions : void 0,
t.owners,
R(t.custom_metadata)
), I = await i.tagsNode(
t.name,
t.tags.map((E) => E)
);
(g === 200 || g === 201) && I.status === 200 ? a({
success: /* @__PURE__ */ y(K, { children: [
"Successfully updated ",
p.type,
" node",
" ",
/* @__PURE__ */ e("a", { href: `/nodes/${p.name}`, children: p.name }),
"!"
] })
}) : a({
failure: `${p.message}`
});
}, ce = /* @__PURE__ */ y("div", { className: "FullNameInput NodeCreationInput", children: [
/* @__PURE__ */ e(q, { name: "name", component: "span" }),
/* @__PURE__ */ e("label", { htmlFor: "FullName", children: "Full Name *" }),
/* @__PURE__ */ e(Ge, { type: "text", name: "name" })
] }), le = (t) => (/* @__PURE__ */ new Set(["transform", "metric", "dimension"])).has(t), de = async (t) => {
var p, I, E, k, O, j, u, L, Z, G, Y, X, ee, te;
const a = await i.getNodeForEditing(t);
if (a === null)
return { message: `Node ${t} does not exist` };
const g = {
name: a.name,
type: a.type.toLowerCase(),
display_name: a.current.displayName,
description: a.current.description,
primary_key: a.current.primaryKey,
query: a.current.query,
tags: a.tags,
mode: a.current.mode.toLowerCase(),
owners: a.owners,
custom_metadata: a.current.customMetadata
};
if (a.type === "METRIC") {
const me = a.current.isDerivedMetric, H = a.current.parents.find(
(V) => V.type !== "METRIC"
);
if (me) {
const ne = (a.current.query || "").match(/SELECT\s+(.+)/is), ae = ne ? ne[1].trim() : ((p = a.current.metricMetadata) == null ? void 0 : p.expression) || "";
return {
...g,
metric_direction: (E = (I = a.current.metricMetadata) == null ? void 0 : I.direction) == null ? void 0 : E.toLowerCase(),
metric_unit: (j = (O = (k = a.current.metricMetadata) == null ? void 0 : k.unit) == null ? void 0 : O.name) == null ? void 0 : j.toLowerCase(),
significant_digits: (u = a.current.metricMetadata) == null ? void 0 : u.significantDigits,
required_dimensions: a.current.requiredDimensions.map(
(ue) => ue.name
),
upstream_node: "",
// Derived metrics have no upstream node
aggregate_expression: ae
};
} else
return {
...g,
metric_direction: (Z = (L = a.current.metricMetadata) == null ? void 0 : L.direction) == null ? void 0 : Z.toLowerCase(),
metric_unit: (X = (Y = (G = a.current.metricMetadata) == null ? void 0 : G.unit) == null ? void 0 : Y.name) == null ? void 0 : X.toLowerCase(),
significant_digits: (ee = a.current.metricMetadata) == null ? void 0 : ee.significantDigits,
required_dimensions: a.current.requiredDimensions.map(
(V) => V.name
),
upstream_node: (H == null ? void 0 : H.name) || "",
aggregate_expression: (te = a.current.metricMetadata) == null ? void 0 : te.expression
};
}
return g;
}, x = (t, a, g) => {
if (t.message !== void 0) {
a(null), g(`Node ${s} does not exist!`);
return;
}
le(t.type) || (a(null), t.type === "cube" && o(`/nodes/${t.name}/edit-cube`), g(`Node ${s} is of type ${t.type} and cannot be edited`));
}, F = (t, a, g, p, I, E, k, O) => {
[
"display_name",
"query",
"type",
"description",
"primary_key",
"mode",
"tags",
"aggregate_expression",
"upstream_node",
"metric_unit",
"metric_direction",
"significant_digits",
"required_dimensions",
"owners",
"custom_metadata"
].forEach((u) => {
if (u === "tags")
a(
u,
t[u].map((L) => L.name)
);
else if (u === "owners")
a(
u,
t[u].map((L) => L.username)
);
else if (u === "custom_metadata") {
const L = t[u] ? JSON.stringify(t[u], null, 2) : "";
a(u, L, !1);
} else
a(u, t[u] || "", !1);
}), g(t), p(
/* @__PURE__ */ e(
Se,
{
defaultValue: t.tags.map((u) => ({ value: u.name, label: u.displayName }))
}
)
), I(
/* @__PURE__ */ e(
De,
{
defaultValue: t.primary_key,
fieldName: "primary_key",
label: "Primary Key",
isMulti: !0
}
)
), t.required_dimensions && k(
/* @__PURE__ */ e(
Me,
{
defaultValue: t.required_dimensions.map((u) => ({ value: u, label: u }))
}
)
), E(
/* @__PURE__ */ e(
Ee,
{
defaultValue: t.upstream_node ? { value: t.upstream_node, label: t.upstream_node } : null
}
)
), t.owners && O(
/* @__PURE__ */ e(
ke,
{
defaultValue: t.owners.map((u) => ({ value: u.username, label: u.username }))
}
)
);
};
return l === S.Edit && r !== !1 ? /* @__PURE__ */ e("div", { className: "mid", children: /* @__PURE__ */ e(
be,
{
namespace: h,
onReadOnlyChange: m
}
) }) : /* @__PURE__ */ y("div", { className: "mid", children: [
/* @__PURE__ */ e(
be,
{
namespace: h,
onReadOnlyChange: m
}
),
/* @__PURE__ */ e("div", { className: "node-builder", children: /* @__PURE__ */ e("div", { className: "node-builder-main", children: /* @__PURE__ */ e(
Ve,
{
initialValues: f,
validate: _,
validateOnChange: !0,
validateOnBlur: !0,
onSubmit: async (t, { setSubmitting: a, setStatus: g }) => {
try {
for (const p of v)
await p(t, { setSubmitting: a, setStatus: g });
} catch (p) {
console.error("Error in submission", p);
} finally {
a(!1);
}
},
children: function(a) {
const {
isSubmitting: g,
status: p,
setFieldValue: I,
errors: E,
touched: k,
isValid: O,
dirty: j
} = a, [u, L] = A([]), [Z, G] = A(null), [Y, X] = A(null), [ee, te] = A(null), [me, H] = A(null), [V, ne] = A(null), [ae, ue] = A("");
return $(() => {
(async () => {
if (l === S.Edit) {
const re = await de(s);
x(re, L, ue), F(
re,
I,
L,
ne,
G,
te,
X,
H
);
}
})().catch(console.error);
}, [I]), /* @__PURE__ */ y(Be, { children: [
Qe(p),
l === S.Edit && ae ? /* @__PURE__ */ e(ut, { message: ae }) : /* @__PURE__ */ y(K, { children: [
/* @__PURE__ */ y("div", { className: "node-builder-form-header", children: [
N,
/* @__PURE__ */ e(ht, {})
] }),
/* @__PURE__ */ y("div", { className: "node-group", children: [
/* @__PURE__ */ y("div", { className: "node-row", children: [
l === S.Add ? /* @__PURE__ */ e(
mt,
{
initialNamespace: d
}
) : w(u),
/* @__PURE__ */ e(pt, {})
] }),
l === S.Add ? ce : "",
/* @__PURE__ */ e(ft, {})
] }),
/* @__PURE__ */ y("div", { className: "node-group", children: [
c === "metric" || (u == null ? void 0 : u.type) === "metric" ? l === S.Edit ? ee : /* @__PURE__ */ e(Ee, {}) : null,
c === "metric" || u.type === "metric" ? /* @__PURE__ */ e(
Ye,
{
djClient: i,
value: u.aggregate_expression ? u.aggregate_expression : ""
}
) : /* @__PURE__ */ e(
lt,
{
djClient: i,
value: u.query ? u.query : ""
}
),
(c === "metric" || u.type === "metric") && /* @__PURE__ */ e(dt, {})
] }),
/* @__PURE__ */ e("div", { className: "node-group", children: c !== "metric" && u.type !== "metric" ? l === S.Edit ? Z : /* @__PURE__ */ e(
De,
{
defaultValue: [],
fieldName: "primary_key",
label: "Primary Key",
isMulti: !0
}
) : l === S.Edit ? Y : /* @__PURE__ */ e(Me, {}) }),
/* @__PURE__ */ e("div", { className: "node-group", children: /* @__PURE__ */ y("div", { className: "node-row", children: [
l === S.Edit ? me : /* @__PURE__ */ e(ke, {}),
l === S.Edit ? V : /* @__PURE__ */ e(Se, {})
] }) }),
Object.entries(n).map(
([Ne, re]) => /* @__PURE__ */ e("div", { className: "node-group", children: /* @__PURE__ */ e(
re,
{
node: u,
action: l,
registerSubmitHandler: (pe, { prepend: Te } = {}) => {
v.includes(pe) || (Te ? v.unshift(pe) : v.push(pe));
}
}
) }, Ne)
),
/* @__PURE__ */ e("div", { className: "node-group node-group-last", children: /* @__PURE__ */ e(
gt,
{
value: u.custom_metadata ? u.custom_metadata : ""
}
) }),
/* @__PURE__ */ e(
"button",
{
type: "submit",
disabled: g || !O,
className: `save-node-btn${g ? " save-node-btn--loading" : ""}`,
children: g ? /* @__PURE__ */ y(K, { children: [
/* @__PURE__ */ e(He, {}),
"Saving..."
] }) : (l === S.Add ? "Create " : "Save ") + (c || "")
}
)
] })
] });
}
}
) }) })
] });
}
export {
vt as AddEditNodePage
};
//# sourceMappingURL=index-BNBtdTGD.js.map