UNPKG

datajunction-ui

Version:
1,445 lines (1,444 loc) 57.8 kB
import { jsx as t, jsxs as l, Fragment as ae } from "react/jsx-runtime"; import { useNavigate as Be, useParams as vt, useSearchParams as Nt } from "react-router-dom"; import _t, { useContext as Ie, useState as g, useRef as fe, useCallback as te, useEffect as B, useMemo as De } from "react"; import { D as Re, cv as Ae, aa as St, aB as ke, bD as Ct, ay as wt, b7 as Dt, af as j, z as kt, a8 as Le, N as Lt, bt as Pt, bs as Tt, bE as Bt, aW as It, bg as Rt } from "./index-n79cxNwC.js"; import { N as At } from "./NodeListActions-BJVAKd6-.js"; const Mt = process.env.REACT_APP_PERSONAL_NAMESPACE_PREFIX || "users"; function Ot(s, n) { if (s && s !== "default") return s; if (!n) return "default"; const a = n.split("@")[0].toLowerCase().replace(/[^a-z0-9_]/g, "_"); return `${Mt}.${a}`; } function zt({ namespace: s }) { const n = Ie(Re).DataJunctionAPI, { currentUser: a } = Ae(), d = Be(), h = Ot(s, a == null ? void 0 : a.username), r = (!s || s === "default") && (a == null ? void 0 : a.username), y = (c) => async (_) => { if (_.preventDefault(), r) try { await n.addNamespace(h); } catch (v) { console.error("Failed to ensure namespace exists:", v); } d(c); }; return /* @__PURE__ */ t( "span", { className: "menu-link", style: { display: "inline-flex", alignItems: "center", margin: 0, width: "130px" }, children: /* @__PURE__ */ t("span", { className: "menu-title", children: /* @__PURE__ */ l("div", { className: "dropdown", children: [ /* @__PURE__ */ t("span", { className: "add_node", children: "+ Add Node" }), /* @__PURE__ */ l("div", { className: "dropdown-content add-node-dropdown-content", children: [ /* @__PURE__ */ t("a", { href: "/create/source", children: /* @__PURE__ */ t("div", { className: "node_type__source node_type_creation_heading", children: "Register Table" }) }), /* @__PURE__ */ t( "a", { href: `/create/transform/${h}`, onClick: y(`/create/transform/${h}`), children: /* @__PURE__ */ t("div", { className: "node_type__transform node_type_creation_heading", children: "Transform" }) } ), /* @__PURE__ */ t( "a", { href: `/create/metric/${h}`, onClick: y(`/create/metric/${h}`), children: /* @__PURE__ */ t("div", { className: "node_type__metric node_type_creation_heading", children: "Metric" }) } ), /* @__PURE__ */ t( "a", { href: `/create/dimension/${h}`, onClick: y(`/create/dimension/${h}`), children: /* @__PURE__ */ t("div", { className: "node_type__dimension node_type_creation_heading", children: "Dimension" }) } ), /* @__PURE__ */ t("a", { href: "/create/tag", children: /* @__PURE__ */ t("div", { className: "entity__tag node_type_creation_heading", children: "Tag" }) }), /* @__PURE__ */ t("a", { href: `/create/cube/${h}`, onClick: y(`/create/cube/${h}`), children: /* @__PURE__ */ t("div", { className: "node_type__cube node_type_creation_heading", children: "Cube" }) }) ] }) ] }) }) } ); } const me = 100, Et = (s) => { const { children: n } = s; return Array.isArray(n) && n.length > me ? /* @__PURE__ */ l(ke.MenuList, { ...s, children: [ n.slice(0, me), /* @__PURE__ */ l( "div", { style: { padding: "6px 12px", fontSize: "11px", color: "#888" }, children: [ "Showing first ", me, " of ", n.length, ". Type to narrow…" ] } ) ] }) : /* @__PURE__ */ t(ke.MenuList, { ...s, children: n }); }; function ne({ label: s, name: n, options: a, value: d, onChange: h, isMulti: r = !1, isClearable: y = !0, placeholder: c = "Select...", minWidth: _ = "100px", flex: v = 1, isLoading: I = !1, testId: N = null, formatOptionLabel: O = void 0, onMenuOpen: T = void 0 }) { const R = r ? d != null && d.length ? a.filter((b) => d.includes(b.value)) : [] : d ? a.find((b) => b.value === d) : null; return /* @__PURE__ */ l( "div", { style: { display: "flex", flexDirection: "column", gap: "2px", flex: v, minWidth: _ }, "data-testid": N, children: [ /* @__PURE__ */ t( "label", { style: { fontSize: "10px", fontWeight: "600", color: "#666", textTransform: "uppercase", letterSpacing: "0.5px" }, children: s } ), /* @__PURE__ */ t( St, { name: n, isClearable: y, isMulti: r, isLoading: I, placeholder: c, onChange: h, onMenuOpen: T, value: R, formatOptionLabel: O, components: { MenuList: Et }, styles: { control: (b) => ({ ...b, minHeight: "32px", height: r ? "auto" : "32px", fontSize: "12px", backgroundColor: "white" }), valueContainer: (b) => ({ ...b, padding: "0 6px" }), input: (b) => ({ ...b, margin: 0, padding: 0 }), indicatorSeparator: () => ({ display: "none" }), dropdownIndicator: (b) => ({ ...b, padding: "4px" }), clearIndicator: (b) => ({ ...b, padding: "4px" }), option: (b) => ({ ...b, fontSize: "12px", padding: "6px 10px" }), multiValue: (b) => ({ ...b, fontSize: "11px" }) }, options: a } ) ] } ); } function Me({ open: s = !1, size: n = 12 }) { return /* @__PURE__ */ t( "svg", { width: n, height: n, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round", style: { transform: s ? "rotate(90deg)" : "none", transition: "transform 0.12s ease" }, children: /* @__PURE__ */ t("polyline", { points: "9 6 15 12 9 18" }) } ); } const ge = "dj.ns.pinned"; function Oe(s) { try { const n = JSON.parse(localStorage.getItem(s)); return Array.isArray(n) ? n.filter((a) => typeof a == "string") : []; } catch { return []; } } function $t(s, n) { try { localStorage.setItem(s, JSON.stringify(n)); } catch { } } function jt() { return Oe(ge); } function Wt(s) { const n = Oe(ge), a = n.includes(s) ? n.filter((d) => d !== s) : [...n, s]; return $t(ge, a), a; } function ze({ nodes: s, depth: n, expanded: a, onToggle: d, onSelect: h }) { return s.map((r) => { var _; const y = ((_ = r.children) == null ? void 0 : _.length) > 0, c = a.has(r.path); return /* @__PURE__ */ l(_t.Fragment, { children: [ /* @__PURE__ */ l( "div", { className: "dj-ns-nav-item", role: "button", tabIndex: 0, title: r.path, style: { paddingLeft: `${n * 14}px` }, onClick: () => h(r.path), onKeyDown: (v) => { v.key === "Enter" && h(r.path); }, children: [ y ? /* @__PURE__ */ t( "button", { type: "button", "aria-label": c ? "Collapse" : "Expand", "aria-expanded": c, onClick: (v) => { v.stopPropagation(), d(r.path); }, style: { width: "18px", minWidth: "18px", flexShrink: 0, padding: 0, margin: 0, border: "none", background: "none", cursor: "pointer", color: "#64748b", display: "inline-flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ t(Me, { open: c }) } ) : /* @__PURE__ */ t("span", { style: { width: "18px", minWidth: "18px", flexShrink: 0 } }), /* @__PURE__ */ t("span", { className: "dj-ns-nav-name", children: r.namespace }) ] } ), y && c ? /* @__PURE__ */ t( ze, { nodes: r.children, depth: n + 1, expanded: a, onToggle: d, onSelect: h } ) : null ] }, r.path); }); } function Ft({ folders: s, onSelect: n }) { const [a, d] = g(() => /* @__PURE__ */ new Set()); return !s || s.length === 0 ? null : /* @__PURE__ */ l("div", { className: "dj-ns-folder-nav", children: [ /* @__PURE__ */ t("div", { className: "dj-ns-tree-heading", children: "Folders" }), /* @__PURE__ */ t( ze, { nodes: s, depth: 0, expanded: a, onToggle: (r) => d((y) => { const c = new Set(y); return c.has(r) ? c.delete(r) : c.add(r), c; }), onSelect: n } ) ] }); } function Ht({ parent: s, onCreate: n }) { const [a, d] = g(!1), [h, r] = g(""), [y, c] = g(null), _ = h.trim().replace(/[^a-zA-Z0-9_]/g, "_"), v = () => { d(!1), r(""), c(null); }, I = async () => { if (!_) { v(); return; } const N = await n(`${s}.${_}`); N != null && N._error ? c(N.message || "Failed to create namespace") : v(); }; return a ? /* @__PURE__ */ l("div", { style: { marginTop: "4px", paddingLeft: "18px", paddingRight: "8px" }, children: [ /* @__PURE__ */ t( "input", { autoFocus: !0, type: "text", value: h, onChange: (N) => r(N.target.value), onKeyDown: (N) => { N.key === "Enter" ? I() : N.key === "Escape" && v(); }, onBlur: () => { h.trim() || v(); }, placeholder: "Folder name", "aria-label": "New sub-namespace name", style: { width: "100%", boxSizing: "border-box", padding: "6px 8px", fontSize: "15px", border: "1px solid #cbd5e1", borderRadius: "6px", outline: "none" } } ), y ? /* @__PURE__ */ t("div", { style: { marginTop: "4px", fontSize: "12px", color: "#b91c1c" }, children: y }) : null ] }) : /* @__PURE__ */ l( "button", { type: "button", className: "dj-ns-nav-item", onClick: () => d(!0), style: { paddingLeft: 0, color: "#64748b" }, children: [ /* @__PURE__ */ t( "span", { style: { width: "18px", minWidth: "18px", flexShrink: 0, display: "inline-flex", alignItems: "center", justifyContent: "center", fontSize: "16px", lineHeight: 1 }, children: "+" } ), /* @__PURE__ */ t("span", { className: "dj-ns-nav-name", children: "New folder" }) ] } ); } function Vt(s, n) { var d; const a = s[n]; return a ? a.__typename === "GitRootConfig" ? { root: n, defaultBranch: a.defaultBranch, activeBranch: a.defaultBranch, isRoot: !0 } : a.__typename === "GitBranchConfig" ? { root: a.parentNamespace, defaultBranch: (d = a.root) == null ? void 0 : d.defaultBranch, activeBranch: a.branch, isRoot: !1 } : null : null; } function Ut({ namespaces: s, hierarchy: n, currentNamespace: a, gitRoots: d, onSelect: h, canCreateNamespace: r = !1, onCreateNamespace: y }) { const [c, _] = g({ "Top-level": !0 }), [v, I] = g(() => jt()), [N, O] = g(""), T = N.trim() !== "", R = T ? Ct(s || [], N) : [], b = !a, X = b ? wt(s || []) : [], W = {}; for (const u of s || []) W[u.namespace] = u.git; const A = new Set(v), z = (u) => !c[u], F = (u) => _((x) => ({ ...x, [u]: !x[u] })), H = (u, x) => { u.stopPropagation(), I(Wt(x)); }, i = (u, x) => /* @__PURE__ */ l( "div", { className: "dj-ns-nav-item", role: "button", tabIndex: 0, title: u, onClick: () => h(u), onKeyDown: (k) => { k.key === "Enter" && h(u); }, children: [ /* @__PURE__ */ t("span", { className: "dj-ns-nav-name", children: u }), /* @__PURE__ */ t( "button", { type: "button", className: `dj-ns-star${A.has(u) ? " pinned" : ""}`, "aria-label": `${A.has(u) ? "Unpin" : "Pin"} ${u}`, "aria-pressed": A.has(u), onClick: (k) => H(k, u), children: A.has(u) ? "★" : "☆" } ) ] }, `${x}-${u}` ), V = (u, x, k) => { const E = z(u); return /* @__PURE__ */ l("div", { children: [ /* @__PURE__ */ l( "button", { type: "button", className: "dj-ns-group-heading", "aria-expanded": E, onClick: () => F(u), children: [ /* @__PURE__ */ t("span", { className: "dj-ns-chevron", children: /* @__PURE__ */ t(Me, { open: E }) }), /* @__PURE__ */ t("span", { className: "dj-ns-group-label", children: u }), /* @__PURE__ */ t("span", { className: "dj-ns-group-count", children: x.length }) ] } ), E ? x.map((se) => i(se, k)) : null ] }, k); }, w = a ? Vt(W, a) : null, U = w != null && w.isRoot && w.defaultBranch ? `${w.root}.${w.defaultBranch}` : a; return /* @__PURE__ */ t("div", { children: b ? /* @__PURE__ */ l(ae, { children: [ /* @__PURE__ */ l("div", { className: "dj-ns-filter", children: [ /* @__PURE__ */ t( "input", { type: "text", className: "dj-ns-filter-input", value: N, onChange: (u) => O(u.target.value), placeholder: "Filter namespaces…", "aria-label": "Filter namespaces" } ), T ? /* @__PURE__ */ t( "button", { type: "button", className: "dj-ns-filter-clear", "aria-label": "Clear filter", title: "Clear filter", onClick: () => O(""), children: "✕" } ) : null ] }), T ? R.length === 0 ? /* @__PURE__ */ l( "div", { style: { padding: "6px", color: "#64748b", fontSize: "12px" }, children: [ 'No namespaces match "', N.trim(), '".' ] } ) : V("Matches", R, "matches") : /* @__PURE__ */ l(ae, { children: [ v.length > 0 ? V("Pinned", v, "pin") : null, X.map( (u) => V( u.label, u.options.map((x) => x.value), `grp-${u.label}` ) ) ] }) ] }) : ( // Folder navigation: the current namespace's sub-namespace tree. Chevron // expands a level in place; clicking a name drills in. Going up is handled // by the header breadcrumb; switching namespace by the filter box // (no-namespace view) or the header switcher. Keyed by the current path so // expansion resets when the tree re-roots. /* @__PURE__ */ l(ae, { children: [ /* @__PURE__ */ t( Ft, { folders: Dt(n || [], U), onSelect: h }, U ), r ? /* @__PURE__ */ t( Ht, { parent: U, onCreate: y } ) : null ] }) ) }); } const Pe = [ "metric", "cube", "dimension", "transform", "source" ], Te = { metric: { bg: "#fad7dd", color: "#a2283e" }, cube: { bg: "#dbafff", color: "#580076" }, dimension: { bg: "#ffefd0", color: "#a96621" }, transform: { bg: "#ccefff", color: "#0063b4" }, source: { bg: "#ccf7e5", color: "#00b368" } }, Ee = [ ["#dbeafe", "#1e40af"], ["#dcfce7", "#166534"], ["#fef3c7", "#92400e"], ["#fce7f3", "#9d174d"], ["#ede9fe", "#5b21b6"], ["#ffedd5", "#9a3412"], ["#fee2e2", "#991b1b"], ["#d1fae5", "#065f46"] ]; function Gt(s) { let n = 0; for (let a = 0; a < s.length; a++) n = n * 31 + s.charCodeAt(a) >>> 0; return n % Ee.length; } const Yt = { name: { label: "Name" }, displayName: { label: "Display name" }, type: { label: "Type" }, status: { label: "Validation", tooltip: "Whether this node validates successfully." }, mode: { label: "Publish state", tooltip: "Whether this node is published or still a draft." }, owners: { label: "Owners" }, updatedAt: { label: "Updated" } }, $e = { padding: "8px 16px" }, M = { ...$e, verticalAlign: "middle" }, Jt = { CUBE: "A curated set of metrics and dimensions for querying together.", DIMENSION: "Descriptive attributes used to group, filter, and join metrics.", METRIC: "A reusable business calculation or measure.", SOURCE: "A physical table registered in DJ as a source node.", TRANSFORM: "A SQL-defined node derived from sources or other DJ nodes." }; function qt(s) { const n = s.lastIndexOf("."); return n === -1 ? { leafName: s, namespacePath: "" } : { leafName: s.slice(n + 1), namespacePath: s.slice(0, n) }; } function Kt(s) { const n = s.mode === "PUBLISHED", a = s.status === "VALID"; return { isPublished: n, isValid: a, publishLabel: n ? "Published" : "Draft", validationLabel: a ? "Valid" : "Needs fixes", publishTooltip: n ? "This version is published and expected to be usable by downstream nodes." : "This version is a draft and may still be incomplete.", validationTooltip: a ? "This node validates successfully." : "This node has validation errors. Open it to see what needs to be fixed.", needsAttention: !n || !a, borderColor: a && n ? "#28a745" : "#d39e00", backgroundColor: a && n ? "#eaf7ee" : "#fff7df", color: a && n ? "#1f7a3a" : "#8a5b00" }; } function Xt(s) { return new Date(s).toLocaleString("en-us", { year: "numeric", month: "short", day: "numeric", hour: "numeric", minute: "2-digit" }); } function Zt(s) { const n = new Date(s), a = Date.now() - n.getTime(), d = Math.abs(a), h = [ ["year", 365 * 24 * 60 * 60 * 1e3], ["month", 30 * 24 * 60 * 60 * 1e3], ["week", 7 * 24 * 60 * 60 * 1e3], ["day", 24 * 60 * 60 * 1e3], ["hour", 60 * 60 * 1e3], ["minute", 60 * 1e3] ]; for (const [r, y] of h) if (d >= y) { const c = Math.round(d / y); return `${c} ${r}${c === 1 ? "" : "s"} ${a >= 0 ? "ago" : "from now"}`; } return "just now"; } function sn() { var we; const s = "ascending", n = "descending", a = [ "name", "displayName", "type", "status", "mode", "owners", "updatedAt" ], d = Ie(Re).DataJunctionAPI, { currentUser: h } = Ae(); var { namespace: r } = vt(); const y = Be(), [c, _] = Nt(), [v, I] = g([]), [N, O] = g([]), [T, R] = g(!1), [b, X] = g(!1), W = fe(!1), A = fe(!1), z = te(async () => { if (!W.current) { W.current = !0, R(!0); try { const e = await d.users(); I(e || []); } finally { R(!1); } } }, [d]), F = te(async () => { if (!A.current) { A.current = !0, X(!0); try { const e = await d.listTags(); O(e || []); } finally { X(!1); } } }, [d]); B(() => { (c.get("editedBy") || c.get("ownedBy")) && z().catch(console.error), c.get("tags") && F().catch(console.error); }, [c, z, F]); const H = te( () => ({ node_type: c.get("type") || "", tags: c.get("tags") ? c.get("tags").split(",") : [], edited_by: c.get("editedBy") || "", mode: c.get("mode") || "", ownedBy: c.get("ownedBy") || "", statuses: c.get("statuses") || "", missingDescription: c.get("missingDescription") === "true", hasMaterialization: c.get("hasMaterialization") === "true", orphanedDimension: c.get("orphanedDimension") === "true" }), [c] ), [i, V] = g(H), [w, U] = g(!1), u = fe(!1); B(() => { if (!u.current) { u.current = !0; return; } V(H()); }, [c, H]); const x = te( (e) => { var p; const o = new URLSearchParams(); e.node_type && o.set("type", e.node_type), (p = e.tags) != null && p.length && o.set("tags", e.tags.join(",")), e.edited_by && o.set("editedBy", e.edited_by), e.mode && o.set("mode", e.mode), e.ownedBy && o.set("ownedBy", e.ownedBy), e.statuses && o.set("statuses", e.statuses), e.missingDescription && o.set("missingDescription", "true"), e.hasMaterialization && o.set("hasMaterialization", "true"), e.orphanedDimension && o.set("orphanedDimension", "true"), _(o); }, [_] ), k = () => { _(new URLSearchParams()); }, E = i.node_type || ((we = i.tags) == null ? void 0 : we.length) || i.edited_by || i.mode || i.ownedBy || i.statuses || i.missingDescription || i.hasMaterialization || i.orphanedDimension, se = [ { id: "my-nodes", label: "My Nodes", filters: { ownedBy: h == null ? void 0 : h.username } }, { id: "needs-attention", label: "Needs Attention", filters: { ownedBy: h == null ? void 0 : h.username, statuses: "INVALID" } }, { id: "drafts", label: "Drafts", filters: { ownedBy: h == null ? void 0 : h.username, mode: "draft" } } ], je = (e) => { const o = { node_type: "", tags: [], edited_by: "", mode: e.filters.mode || "", ownedBy: e.filters.ownedBy || "", statuses: e.filters.statuses || "", missingDescription: e.filters.missingDescription || !1, hasMaterialization: e.filters.hasMaterialization || !1, orphanedDimension: e.filters.orphanedDimension || !1 }; x(o); }, Z = (e) => { var p; const o = e.filters; return (o.ownedBy || "") === (i.ownedBy || "") && (o.statuses || "") === (i.statuses || "") && (o.mode || "") === (i.mode || "") && !i.node_type && !((p = i.tags) != null && p.length) && !i.edited_by && !i.missingDescription && !i.hasMaterialization && !i.orphanedDimension; }, [ye, We] = g({ namespace: r || "", nodes: [] }), [ie, be] = g(!1), [Fe, He] = g([]), [xe, Ve] = g([]), [Ue, Ge] = g(/* @__PURE__ */ new Set()), [f, Ye] = g(void 0), [L, Je] = g({ key: "updatedAt", direction: n }), [ve, oe] = g(null), [Ne, re] = g(null), [_e, qe] = g(!0), [Se, Ke] = g(!0), [Xe, Ze] = g(!0), [Qe, et] = g(!0), [le, tt] = g(""), [de, nt] = g(""); B(() => { const e = setTimeout(() => nt(le.trim()), 300); return () => clearTimeout(e); }, [le]), B(() => { oe(null), re(null); }, [de]); const [ce, pe] = g(null), [at, st] = g(void 0), P = f !== void 0, G = P && !!(f != null && f.github_repo_path) && (f == null ? void 0 : f.git_root_namespace) === r, Q = at === !1, it = !!(f != null && f.github_repo_path || f != null && f.branch_namespace), ot = P && !it, rt = async (e) => { var p, m; const o = await d.addNamespace(e); return o.status === 200 || o.status === 201 ? ((p = d.invalidateNamespacesCache) == null || p.call(d), y(`/namespaces/${e}`), {}) : { _error: !0, message: ((m = o.json) == null ? void 0 : m.message) || "Failed to create namespace" }; }, he = G && !(f != null && f.git_branch), Y = he && (f != null && f.default_branch) ? `${r}.${f.default_branch}` : r; B(() => { he && (f != null && f.default_branch) && y(`/namespaces/${r}.${f.default_branch}`, { replace: !0 }); }, [he, f, r, y]), B(() => { if (!P || G && (f != null && f.default_branch)) return; let e = !1; if (!Y) { pe(null); return; } return d.nodeTypeCounts(Y, Pe).then((o) => { e || pe(o); }).catch(() => { e || pe(null); }), () => { e = !0; }; }, [d, Y, P, G, f]); const lt = (e) => { let o = s; L.key === e && L.direction === s && (o = n), (L.key !== e || L.direction !== o) && Je({ key: e, direction: o }); }, dt = (e) => { if (L.key === e) return L.direction; }, ct = (e) => { const o = [], p = /* @__PURE__ */ new Map(), m = /* @__PURE__ */ new Map(); for (const S of e) { const D = S.namespace.split("."); let $ = o, J = p, q = ""; for (const ee of D) { q += ee; let K = J.get(ee); K || (K = { namespace: ee, children: [], path: q }, $.push(K), J.set(ee, K), m.set(q, /* @__PURE__ */ new Map())), $ = K.children, J = m.get(q), q += "."; } } const C = (S) => { S.sort((D, $) => D.namespace.localeCompare($.namespace)), S.forEach((D) => C(D.children)); }; return C(o), o; }; B(() => { (async () => { const p = (await d.listNamespacesWithGit()).filter((S) => !Rt(S.namespace)); Ve(p); const m = ct(p); He(m); const C = new Set( p.filter((S) => { var D; return ((D = S.git) == null ? void 0 : D.__typename) === "GitRootConfig"; }).map((S) => S.namespace) ); Ge(C); })().catch(console.error); }, [d]), B(() => { if (!P || G && (f != null && f.default_branch)) return; (async () => { be(!1); const o = { ownedBy: i.ownedBy || null, statuses: i.statuses ? [i.statuses] : null, missingDescription: i.missingDescription, hasMaterialization: i.hasMaterialization, orphanedDimension: i.orphanedDimension, // The table shows every node under this namespace (all descendants), // matching the rail's per-type counts. The rail's Folders drill DOWN to // re-scope; search narrows within the current scope. search: de || null }, p = await d.listNodesForLanding( Y, i.node_type ? [i.node_type.toUpperCase()] : [], i.tags, i.edited_by, ve, Ne, 50, L, i.mode ? i.mode.toUpperCase() : null, o ); We({ namespace: r, nodes: p.data ? p.data.findNodesPaginated.edges.map((m) => m.node) : [] }), p.data && (qe( p.data ? p.data.findNodesPaginated.pageInfo.startCursor : "" ), Ke( p.data ? p.data.findNodesPaginated.pageInfo.endCursor : "" ), et( p.data ? p.data.findNodesPaginated.pageInfo.hasPrevPage : !1 ), Ze( p.data ? p.data.findNodesPaginated.pageInfo.hasNextPage : !1 )), be(!0); })().catch(console.error); }, [ d, i, ve, Ne, L.key, L.direction, Y, r, de, P, G, f ]); const pt = () => { Se && (re(Se), oe(null)); }, ht = () => { _e && (re(null), oe(_e)); }, ut = { metric: "Metric", cube: "Cube", dimension: "Dimension", transform: "Transform", source: "Source" }, ft = Pe.map((e) => ({ value: e, label: ut[e], count: (ce == null ? void 0 : ce[e]) ?? null })), mt = (e, o) => { var m, C; const p = e.count != null ? /* @__PURE__ */ t( "span", { style: { backgroundColor: ((m = Te[e.value]) == null ? void 0 : m.bg) ?? "#f1f5f9", color: ((C = Te[e.value]) == null ? void 0 : C.color) ?? "#475569", borderRadius: "8px", padding: "1px 8px", fontSize: "11px", fontWeight: 600, fontVariantNumeric: "tabular-nums" }, children: e.count } ) : null; return /* @__PURE__ */ l( "span", { style: { display: "flex", alignItems: "center", gap: "8px", justifyContent: (o == null ? void 0 : o.context) === "menu" ? "space-between" : "flex-start", width: (o == null ? void 0 : o.context) === "menu" ? "100%" : "auto" }, children: [ /* @__PURE__ */ t("span", { children: e.label }), p ] } ); }, gt = [ { value: "published", label: "Published" }, { value: "draft", label: "Draft" } ], yt = [ { value: "VALID", label: "Valid" }, { value: "INVALID", label: "Needs fixes" } ], Ce = De( () => v.map((e) => ({ value: e.username, label: e.username })), [v] ), bt = De( () => N.map((e) => ({ value: e.name, label: e.display_name })), [N] ), xt = ie ? ye.nodes.length > 0 ? ye.nodes.map((e) => { const { leafName: o, namespacePath: p } = qt(e.name), m = Kt(e.current), C = e.owners || []; return /* @__PURE__ */ l( "tr", { style: { backgroundColor: m.needsAttention ? "#fffbeb" : void 0 }, children: [ /* @__PURE__ */ l( "td", { style: { ...$e, maxWidth: "300px", overflow: "hidden", whiteSpace: "nowrap", textOverflow: "ellipsis" }, children: [ /* @__PURE__ */ l("div", { children: [ /* @__PURE__ */ t( "a", { href: "/nodes/" + e.name, className: "link-table", title: e.name, style: { fontWeight: 600, color: "#334155" }, children: o } ), /* @__PURE__ */ t( "span", { className: "rounded-pill badge bg-secondary-soft", style: { marginLeft: "0.5rem" }, children: e.currentVersion } ) ] }), p ? /* @__PURE__ */ t( "div", { title: p, style: { color: "#64748b", fontSize: "12px", marginTop: "2px", overflow: "hidden", textOverflow: "ellipsis" }, children: p } ) : null ] } ), /* @__PURE__ */ t( "td", { style: { ...M, maxWidth: "250px", overflow: "hidden", whiteSpace: "nowrap", textOverflow: "ellipsis", color: "#64748b" }, children: /* @__PURE__ */ t( "a", { href: "/nodes/" + e.name, className: "link-table", style: { color: "#475569", fontWeight: 400 }, children: e.type !== "source" ? e.current.displayName : "" } ) } ), /* @__PURE__ */ t("td", { style: M, children: /* @__PURE__ */ t(j, { content: Jt[e.type], children: /* @__PURE__ */ t( "span", { className: "node_type__" + e.type.toLowerCase() + " badge node_type", children: e.type } ) }) }), /* @__PURE__ */ t("td", { style: M, children: /* @__PURE__ */ t(j, { content: m.validationTooltip, children: /* @__PURE__ */ t( "span", { style: { display: "inline-flex", alignItems: "center", borderRadius: "999px", border: `1px solid ${m.isValid ? "#28a745" : "#d39e00"}`, backgroundColor: m.isValid ? "#eaf7ee" : "#fff7df", color: m.isValid ? "#1f7a3a" : "#8a5b00", fontWeight: "600", fontSize: "12px", lineHeight: 1.2, padding: "6px 10px", whiteSpace: "nowrap" }, children: m.validationLabel } ) }) }), /* @__PURE__ */ t("td", { style: M, children: /* @__PURE__ */ t(j, { content: m.publishTooltip, children: /* @__PURE__ */ t( "span", { style: { display: "inline-flex", alignItems: "center", borderRadius: "999px", border: `1px solid ${m.isPublished ? "#28a745" : "#d39e00"}`, backgroundColor: m.isPublished ? "#eaf7ee" : "#fff7df", color: m.isPublished ? "#1f7a3a" : "#8a5b00", fontWeight: "600", fontSize: "12px", lineHeight: 1.2, padding: "6px 10px", whiteSpace: "nowrap" }, children: m.publishLabel } ) }) }), /* @__PURE__ */ t("td", { style: M, children: C.length > 0 && /* @__PURE__ */ t("div", { style: { display: "flex", gap: "2px" }, children: C.slice(0, 3).map((S) => { const D = S.username.split("@")[0].slice(0, 2).toUpperCase(), [$, J] = Ee[Gt(S.username)]; return /* @__PURE__ */ t( "span", { title: S.username, style: { display: "inline-flex", alignItems: "center", justifyContent: "center", width: "24px", height: "24px", borderRadius: "50%", backgroundColor: $, color: J, fontSize: "9px", fontWeight: "600", flexShrink: 0 }, children: D }, S.username ); }) }) }), /* @__PURE__ */ t("td", { style: M, children: /* @__PURE__ */ t(j, { content: Xt(e.current.updatedAt), children: /* @__PURE__ */ t("span", { className: "status", children: Zt(e.current.updatedAt) }) }) }), Q && /* @__PURE__ */ t("td", { style: M, children: /* @__PURE__ */ t(At, { nodeName: e == null ? void 0 : e.name }) }) ] }, e.name ); }) : /* @__PURE__ */ t("tr", { children: /* @__PURE__ */ t("td", { colSpan: a.length + (Q ? 1 : 0), children: /* @__PURE__ */ t( "span", { style: { display: "block", marginTop: "2rem", marginLeft: "2rem", fontSize: "16px" }, children: E ? /* @__PURE__ */ l(ae, { children: [ "No nodes match the current filters.", /* @__PURE__ */ t( "a", { href: "#", onClick: (e) => { e.preventDefault(), k(); }, style: { marginLeft: "0.5rem" }, children: "Clear filters" } ) ] }) : "No nodes in this namespace yet." } ) }) }) : /* @__PURE__ */ t("tr", { children: /* @__PURE__ */ t("td", { children: /* @__PURE__ */ t("span", { style: { display: "block", marginTop: "2rem" }, children: /* @__PURE__ */ t(kt, {}) }) }) }), ue = [ !!i.edited_by, i.missingDescription, i.hasMaterialization, i.orphanedDimension ].filter(Boolean).length; return /* @__PURE__ */ t("div", { className: "mid", children: /* @__PURE__ */ t("div", { className: "card", children: /* @__PURE__ */ l("div", { className: "card-header", children: [ /* @__PURE__ */ t( "div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "1rem" }, children: /* @__PURE__ */ t("h2", { style: { margin: 0 }, children: "Browse" }) } ), P && /* @__PURE__ */ l( "div", { style: { marginBottom: "1rem", padding: "1rem", backgroundColor: "#f8fafc", borderRadius: "8px" }, children: [ /* @__PURE__ */ t( "div", { style: { display: "flex", alignItems: "center", gap: "12px", marginBottom: "12px" }, children: /* @__PURE__ */ l( "div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [ /* @__PURE__ */ t( "span", { style: { fontSize: "11px", fontWeight: "600", textTransform: "uppercase", letterSpacing: "0.5px", color: "#64748b" }, children: "Quick" } ), se.map((e) => /* @__PURE__ */ t( "button", { onClick: () => je(e), style: { padding: "4px 10px", fontSize: "11px", border: "1px solid", borderColor: Z(e) ? "#1976d2" : "#ddd", borderRadius: "12px", backgroundColor: Z(e) ? "#e3f2fd" : "white", color: Z(e) ? "#1976d2" : "#666", cursor: "pointer", fontWeight: Z(e) ? "600" : "400" }, children: e.label }, e.id )), E && /* @__PURE__ */ t( "button", { onClick: k, style: { padding: "4px 10px", fontSize: "11px", border: "none", backgroundColor: "transparent", color: "#dc3545", cursor: "pointer" }, children: "Clear all ×" } ) ] } ) } ), /* @__PURE__ */ l( "div", { style: { display: "flex", alignItems: "flex-end", gap: "12px" }, children: [ /* @__PURE__ */ t( ne, { label: "Type", name: "type", options: ft, value: i.node_type, onChange: (e) => x({ ...i, node_type: (e == null ? void 0 : e.value) || "" }), formatOptionLabel: mt, flex: 1, minWidth: "80px", testId: "select-node-type" } ), /* @__PURE__ */ t( ne, { label: "Owner", name: "owner", options: Ce, value: i.ownedBy, onChange: (e) => x({ ...i, ownedBy: (e == null ? void 0 : e.value) || "" }), isLoading: T, onMenuOpen: z, flex: 1, minWidth: "80px" } ), /* @__PURE__ */ t( ne, { label: "Tags", name: "tags", options: bt, value: i.tags, onChange: (e) => x({ ...i, tags: e ? e.map((o) => o.value) : [] }), isMulti: !0, isLoading: b, onMenuOpen: F, flex: 1.2, minWidth: "100px", testId: "select-tag" } ), /* @__PURE__ */ t( Le, { label: "Publish state", options: gt, value: i.mode, onChange: (e) => x({ ...i, mode: e }) } ), /* @__PURE__ */ t( Le, { label: "Validation", options: yt, value: i.statuses, onChange: (e) => x({ ...i, statuses: e }) } ), /* @__PURE__ */ l( "div", { style: { position: "relative", flex: 0, minWidth: "auto" }, children: [ /* @__PURE__ */ l( "div", { style: { display: "flex", flexDirection: "column", gap: "2px" }, children: [ /* @__PURE__ */ t( "label", { style: { fontSize: "10px", fontWeight: "600", color: "#666", textTransform: "uppercase", letterSpacing: "0.5px" }, children: "More filters" } ), /* @__PURE__ */ l( "button", { onClick: () => U(!w), style: { height: "32px", minHeight: "32px", padding: "0 12px", fontSize: "12px", border: "1px solid #ccc", borderRadius: "4px", backgroundColor: ue > 0 ? "#e3f2fd" : "white", color: "#666", cursor: "pointer", display: "flex", alignItems: "center", gap: "4px", whiteSpace: "nowrap" }, children: [ ue > 0 ? `${ue} active` : "Filters", /* @__PURE__ */ t("span", { style: { fontSize: "8px" }, children: w ? "▲" : "▼" }) ] } ) ] } ), w && /* @__PURE__ */ l( "div", { style: { position: "absolute", top: "100%", right: 0, marginTop: "4px", padding: "12px", backgroundColor: "white", border: "1px solid #ddd", borderRadius: "8px", boxShadow: "0 4px 12px rgba(0,0,0,0.15)", zIndex: 1e3, minWidth: "320px" }, children: [ /* @__PURE__ */ t( "div", { style: { display: "grid", gap: "12px", marginBottom: "12px" }, children: /* @__PURE__ */ t( ne, { label: "Edited By", name: "editedBy", options: Ce, value: i.edited_by, onChange: (e) => x({ ...i, edited_by: (e == null ? void 0 : e.value) || "" }), isLoading: T, onMenuOpen: z, flex: "none", minWidth: "100%", testId: "select-user" } ) } ), /* @__PURE__ */ l( "div", { style: { borderTop: "1px solid #e2e8f0", paddingTop: "12px" }, children: [ /* @__PURE__ */ l( "label", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px", color: "#444", marginBottom: "8px", cursor: "pointer" }, children: [ /* @__PURE__ */ t( "input", { type: "checkbox", checked: i.missingDescription, onChange: (e) => x({ ...i, missingDescription: e.target.checked }) } ), "Missing Description" ] } ), /* @__PURE__ */ l( "label", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px", color: "#444", marginBottom: "8px", cursor: "pointer" }, children: [ /* @__PURE__ */ t( "input", { type: "checkbox", checked: i.orphanedDimension, onChange: (e) => x({ ...i, orphanedDimension: e.target.checked }) } ), "Orphaned Dimensions" ] } ), /* @__PURE__ */ l( "label", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px", color: "#444", cursor: "pointer" }, children: [ /* @__PURE__ */ t( "input", { type: "checkbox", checked: i.hasMaterialization, onChange: (e) => x({ ...i,