datajunction-ui
Version:
DataJunction UI
1,445 lines (1,444 loc) • 57.8 kB
JavaScript
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,