@quantumwake/terminal-ux-dashboard-components 0.1.2 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -38,6 +38,7 @@ function useCapabilities() {
38
38
  canDelete: !!c.deleteDashboard,
39
39
  canAnalyze: !!c.analyzeDataset,
40
40
  canRefine: !!c.refineDashboard,
41
+ canNew: !!c.newDashboard,
41
42
  canAddPanel: !!c.addPanel,
42
43
  canEditPanels: !!c.removePanel
43
44
  };
@@ -648,18 +649,19 @@ var renderCell = (v) => {
648
649
  if (typeof v === "boolean") return v ? "true" : "false";
649
650
  return String(v);
650
651
  };
651
- function SqlConsole({ columns = [], stateId }) {
652
+ function SqlConsole({ columns = [], stateId, defaultColumnsOpen = false, defaultSql = DEFAULT_SQL, autoRun = false }) {
652
653
  const { theme, runQuery } = useDashboard();
653
- const [sql, setSql] = react.useState(DEFAULT_SQL);
654
+ const [sql, setSql] = react.useState(defaultSql);
654
655
  const [result, setResult] = react.useState(null);
655
656
  const [error, setError] = react.useState(null);
656
657
  const [running, setRunning] = react.useState(false);
657
- const run = async () => {
658
- if (running || !sql.trim()) return;
658
+ const [columnsOpen, setColumnsOpen] = react.useState(defaultColumnsOpen);
659
+ const run = async (text = sql) => {
660
+ if (running || !text.trim()) return;
659
661
  setRunning(true);
660
662
  setError(null);
661
663
  try {
662
- const data = await runQuery(sql, stateId);
664
+ const data = await runQuery(text, stateId);
663
665
  setResult({ columns: data.columns || [], rows: data.rows || [] });
664
666
  } catch (err) {
665
667
  setResult(null);
@@ -668,6 +670,11 @@ function SqlConsole({ columns = [], stateId }) {
668
670
  setRunning(false);
669
671
  }
670
672
  };
673
+ react.useEffect(() => {
674
+ if (!autoRun || !stateId) return;
675
+ setSql(defaultSql);
676
+ void run(defaultSql);
677
+ }, [stateId, autoRun, defaultSql]);
671
678
  const onKeyDown = (e) => {
672
679
  if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
673
680
  e.preventDefault();
@@ -708,19 +715,34 @@ function SqlConsole({ columns = [], stateId }) {
708
715
  placeholder: "SELECT ... FROM data"
709
716
  }
710
717
  ),
711
- columns.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 flex flex-wrap gap-1", children: columns.map((c) => /* @__PURE__ */ jsxRuntime.jsxs(
712
- "button",
713
- {
714
- title: `Insert ${c.name}`,
715
- onClick: () => setSql((s) => `${s}${s.endsWith(" ") || s.endsWith("\n") || !s ? "" : " "}${c.name}`),
716
- className: "px-1.5 py-0.5 border border-midnight-border text-[11px] font-mono text-midnight-text-muted hover:bg-midnight-raised transition-colors",
717
- children: [
718
- c.name,
719
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-50 ml-1", children: c.type })
720
- ]
721
- },
722
- c.name
723
- )) })
718
+ columns.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2", children: [
719
+ /* @__PURE__ */ jsxRuntime.jsxs(
720
+ "button",
721
+ {
722
+ onClick: () => setColumnsOpen((o) => !o),
723
+ className: "flex items-center gap-1 text-[11px] font-mono text-midnight-text-muted hover:text-midnight-text-body transition-colors",
724
+ children: [
725
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: `w-3 h-3 transition-transform ${columnsOpen ? "" : "-rotate-90"}` }),
726
+ "Columns (",
727
+ columns.length,
728
+ ")"
729
+ ]
730
+ }
731
+ ),
732
+ columnsOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1.5 flex flex-wrap gap-1", children: columns.map((c) => /* @__PURE__ */ jsxRuntime.jsxs(
733
+ "button",
734
+ {
735
+ title: `Insert ${c.name}`,
736
+ onClick: () => setSql((s) => `${s}${s.endsWith(" ") || s.endsWith("\n") || !s ? "" : " "}${c.name}`),
737
+ className: "px-1.5 py-0.5 border border-midnight-border text-[11px] font-mono text-midnight-text-muted hover:bg-midnight-raised transition-colors",
738
+ children: [
739
+ c.name,
740
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-50 ml-1", children: c.type })
741
+ ]
742
+ },
743
+ c.name
744
+ )) })
745
+ ] })
724
746
  ] }),
725
747
  (result || error) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex items-center gap-4 px-3 py-1.5 border-b ${theme.border} text-xs font-mono shrink-0 ${error ? "text-red-400" : "text-midnight-text-muted"}`, children: error ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1.5", children: [
726
748
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "w-3.5 h-3.5" }),
@@ -1611,6 +1633,11 @@ function ModeTab({ active, onClick, icon: Icon, label }) {
1611
1633
  }
1612
1634
  );
1613
1635
  }
1636
+ var MODE_TABS = [
1637
+ { mode: "dashboard", icon: lucideReact.Sparkles, label: "Dashboard" },
1638
+ { mode: "builder", icon: lucideReact.BarChart3, label: "Chart Builder" },
1639
+ { mode: "sql", icon: lucideReact.Terminal, label: "SQL" }
1640
+ ];
1614
1641
  function DataExplorer({
1615
1642
  records,
1616
1643
  columns,
@@ -1622,12 +1649,15 @@ function DataExplorer({
1622
1649
  savedDashboards = [],
1623
1650
  loading = false,
1624
1651
  analyzing = false,
1652
+ defaultMode = "dashboard",
1653
+ tabs = ["dashboard", "builder", "sql"],
1625
1654
  onSelectState,
1626
1655
  onRefreshStates
1627
1656
  }) {
1628
- const { theme, addPanel, saveDashboard, listDashboards, searchDashboards, loadDashboard, deleteDashboard, analyzeDataset, refineDashboard } = useDashboard();
1657
+ const { theme, newDashboard, addPanel, saveDashboard, listDashboards, searchDashboards, loadDashboard, deleteDashboard, analyzeDataset, refineDashboard } = useDashboard();
1629
1658
  const caps = useCapabilities();
1630
- const [mode, setMode] = react.useState("dashboard");
1659
+ const visibleTabs = MODE_TABS.filter((t) => tabs.includes(t.mode));
1660
+ const [mode, setMode] = react.useState(tabs.includes(defaultMode) ? defaultMode : tabs[0] ?? "sql");
1631
1661
  const [fullscreen, setFullscreen] = react.useState(false);
1632
1662
  const [showSaved, setShowSaved] = react.useState(false);
1633
1663
  const [saveName, setSaveName] = react.useState("");
@@ -1692,9 +1722,22 @@ function DataExplorer({
1692
1722
  )
1693
1723
  ] }),
1694
1724
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 mb-3 shrink-0", children: [
1695
- /* @__PURE__ */ jsxRuntime.jsx(ModeTab, { active: mode === "dashboard", onClick: () => setMode("dashboard"), icon: lucideReact.Sparkles, label: "Dashboard" }),
1696
- /* @__PURE__ */ jsxRuntime.jsx(ModeTab, { active: mode === "builder", onClick: () => setMode("builder"), icon: lucideReact.BarChart3, label: "Chart Builder" }),
1697
- /* @__PURE__ */ jsxRuntime.jsx(ModeTab, { active: mode === "sql", onClick: () => setMode("sql"), icon: lucideReact.Terminal, label: "SQL" }),
1725
+ visibleTabs.map((t) => /* @__PURE__ */ jsxRuntime.jsx(ModeTab, { active: mode === t.mode, onClick: () => setMode(t.mode), icon: t.icon, label: t.label }, t.mode)),
1726
+ caps.canNew && /* @__PURE__ */ jsxRuntime.jsxs(
1727
+ "button",
1728
+ {
1729
+ onClick: () => {
1730
+ newDashboard?.();
1731
+ setMode("builder");
1732
+ },
1733
+ title: "Start a new dashboard",
1734
+ className: "flex items-center gap-1 px-2 py-1 text-xs font-mono border border-midnight-border text-midnight-text-muted hover:bg-midnight-raised hover:text-midnight-accent transition-colors",
1735
+ children: [
1736
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "w-3.5 h-3.5" }),
1737
+ " New Dashboard"
1738
+ ]
1739
+ }
1740
+ ),
1698
1741
  showPersistence && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 ml-auto", children: [
1699
1742
  caps.canSave && showSaveInput && /* @__PURE__ */ jsxRuntime.jsx(
1700
1743
  "input",
@@ -1763,14 +1806,39 @@ function DataExplorer({
1763
1806
  )
1764
1807
  ] }, d.id))
1765
1808
  ] }),
1766
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: `border ${theme.border} bg-midnight-surface flex-1 flex flex-col min-h-0`, children: mode === "sql" ? /* @__PURE__ */ jsxRuntime.jsx(SqlConsole, { columns, stateId: activeStateId ?? void 0 }) : mode === "builder" ? /* @__PURE__ */ jsxRuntime.jsx(ChartBuilder, { records, columns, stateId: activeStateId ?? void 0, onSave: addPanel }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1809
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `border ${theme.border} bg-midnight-surface flex-1 flex flex-col min-h-0`, children: mode === "sql" ? /* @__PURE__ */ jsxRuntime.jsx(SqlConsole, { columns, stateId: activeStateId ?? void 0 }) : mode === "builder" ? /* @__PURE__ */ jsxRuntime.jsx(
1810
+ ChartBuilder,
1811
+ {
1812
+ records,
1813
+ columns,
1814
+ stateId: activeStateId ?? void 0,
1815
+ onSave: addPanel ? (panel) => {
1816
+ addPanel(panel);
1817
+ setMode("dashboard");
1818
+ } : void 0
1819
+ }
1820
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1767
1821
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 overflow-auto", children: dashboard ? /* @__PURE__ */ jsxRuntime.jsx(DashboardRenderer, { dashboard, records, columns }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center h-full gap-4 text-midnight-text-muted", children: [
1768
1822
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "w-8 h-8" }),
1769
1823
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm", children: caps.canAnalyze ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1770
1824
  "Click ",
1771
1825
  /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Auto-Analyze" }),
1772
1826
  " to generate an AI dashboard"
1773
- ] }) : "No dashboard to display" })
1827
+ ] }) : caps.canNew ? "Start a new dashboard, then add charts from the Chart Builder" : "No dashboard to display" }),
1828
+ caps.canNew && /* @__PURE__ */ jsxRuntime.jsxs(
1829
+ "button",
1830
+ {
1831
+ onClick: () => {
1832
+ newDashboard?.();
1833
+ setMode("builder");
1834
+ },
1835
+ className: "flex items-center gap-1.5 px-3 py-1.5 text-xs font-mono border border-midnight-accent text-midnight-accent hover:bg-midnight-accent/10 transition-colors",
1836
+ children: [
1837
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "w-4 h-4" }),
1838
+ " Create Dashboard"
1839
+ ]
1840
+ }
1841
+ )
1774
1842
  ] }) }),
1775
1843
  dashboard && caps.canRefine && refineDashboard && /* @__PURE__ */ jsxRuntime.jsx(ChatInput, { onSend: refineDashboard, loading: analyzing, theme })
1776
1844
  ] }) })