@burdenoff/microfe-bigconsole 2026.803.4 → 2026.804.2
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/bigconsole/components/datasink/ImportDataGuideDialog.js +66 -0
- package/dist/bigconsole/components/datasink/ImportDataGuideDialog.js.map +1 -0
- package/dist/bigconsole/components/parser/DataSinkSelector.js +129 -106
- package/dist/bigconsole/components/parser/DataSinkSelector.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserSelector.js +183 -144
- package/dist/bigconsole/components/parser/ParserSelector.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetWrapper.js +3 -1
- package/dist/bigconsole/components/widgets/WidgetWrapper.js.map +1 -1
- package/dist/bigconsole/components/widgets/form/FormWidget.js +155 -121
- package/dist/bigconsole/components/widgets/form/FormWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/form/index.js +2 -2
- package/dist/bigconsole/components/widgets/index.js +2 -0
- package/dist/bigconsole/contexts/NavigationContext.js +6 -2
- package/dist/bigconsole/contexts/NavigationContext.js.map +1 -1
- package/dist/bigconsole/pages/DataSinkViewPage.js +136 -101
- package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { memo as e } from "react";
|
|
2
|
+
import { ArrowRight as t, Upload as n, Workflow as r } from "lucide-react";
|
|
3
|
+
import { Button as i, Dialog as a, DialogContent as o, DialogDescription as s, DialogFooter as c, DialogHeader as l, DialogTitle as u } from "@burdenoff/fe-libs/ui";
|
|
4
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
5
|
+
//#region src/bigconsole/components/datasink/ImportDataGuideDialog.tsx
|
|
6
|
+
var p = [
|
|
7
|
+
"Go to the Workflows page and create a new workflow.",
|
|
8
|
+
"Add a source node to fetch your data, then a node that writes to this data sink.",
|
|
9
|
+
"Run the workflow — the imported data appears here in the data preview."
|
|
10
|
+
], m = e(function({ isOpen: e, dataSinkName: m, onClose: h, onGoToWorkflows: g }) {
|
|
11
|
+
return /* @__PURE__ */ d(a, {
|
|
12
|
+
open: e,
|
|
13
|
+
onOpenChange: (e) => !e && h(),
|
|
14
|
+
children: /* @__PURE__ */ f(o, {
|
|
15
|
+
className: "sm:max-w-lg",
|
|
16
|
+
children: [
|
|
17
|
+
/* @__PURE__ */ f(l, { children: [/* @__PURE__ */ f("div", {
|
|
18
|
+
className: "flex items-center gap-3",
|
|
19
|
+
children: [/* @__PURE__ */ d("div", {
|
|
20
|
+
className: "flex size-10 items-center justify-center rounded-lg bg-action-primary-bg/10",
|
|
21
|
+
children: /* @__PURE__ */ d(n, { className: "size-5 text-action-primary-bg" })
|
|
22
|
+
}), /* @__PURE__ */ d(u, { children: "Import data" })]
|
|
23
|
+
}), /* @__PURE__ */ d(s, { children: m ? `Populate “${m}” with a workflow` : "Populate this data sink with a workflow" })] }),
|
|
24
|
+
/* @__PURE__ */ f("div", {
|
|
25
|
+
className: "py-2 space-y-4",
|
|
26
|
+
children: [/* @__PURE__ */ f("p", {
|
|
27
|
+
className: "text-sm text-text-primary leading-relaxed",
|
|
28
|
+
children: [
|
|
29
|
+
"Data is imported into a data sink by building a ",
|
|
30
|
+
/* @__PURE__ */ d("strong", { children: "workflow" }),
|
|
31
|
+
". A workflow fetches data from your source (an API, database, file, or app) and writes it into this data sink so your dashboards can read it."
|
|
32
|
+
]
|
|
33
|
+
}), /* @__PURE__ */ d("ol", {
|
|
34
|
+
className: "space-y-3",
|
|
35
|
+
children: p.map((e, t) => /* @__PURE__ */ f("li", {
|
|
36
|
+
className: "flex items-start gap-3",
|
|
37
|
+
children: [/* @__PURE__ */ d("span", {
|
|
38
|
+
className: "flex size-6 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-xs font-semibold text-text-primary",
|
|
39
|
+
children: t + 1
|
|
40
|
+
}), /* @__PURE__ */ d("p", {
|
|
41
|
+
className: "text-sm text-text-secondary leading-relaxed",
|
|
42
|
+
children: e
|
|
43
|
+
})]
|
|
44
|
+
}, t))
|
|
45
|
+
})]
|
|
46
|
+
}),
|
|
47
|
+
/* @__PURE__ */ f(c, { children: [/* @__PURE__ */ d(i, {
|
|
48
|
+
variant: "outline",
|
|
49
|
+
onClick: h,
|
|
50
|
+
children: "Cancel"
|
|
51
|
+
}), /* @__PURE__ */ f(i, {
|
|
52
|
+
onClick: g,
|
|
53
|
+
children: [
|
|
54
|
+
/* @__PURE__ */ d(r, { className: "size-4" }),
|
|
55
|
+
"Go to Workflows",
|
|
56
|
+
/* @__PURE__ */ d(t, { className: "size-4" })
|
|
57
|
+
]
|
|
58
|
+
})] })
|
|
59
|
+
]
|
|
60
|
+
})
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
//#endregion
|
|
64
|
+
export { m as default };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=ImportDataGuideDialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImportDataGuideDialog.js","names":[],"sources":["../../../../src/bigconsole/components/datasink/ImportDataGuideDialog.tsx"],"sourcesContent":["/**\n * Guides a user on how to import data into a DataSink (via a workflow) and\n * navigates them to the Workflows page. Named to avoid confusion with\n * ImportDataSinkDialog, which imports a DataSink config from JSON.\n */\n\nimport { type FC, memo } from 'react';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Button,\n} from '@burdenoff/fe-libs/ui';\nimport { Upload, Workflow, ArrowRight } from 'lucide-react';\n\nexport interface ImportDataGuideDialogProps {\n /** Whether dialog is open */\n isOpen: boolean;\n /** DataSink name for context */\n dataSinkName?: string;\n /** Callback when dialog is closed */\n onClose: () => void;\n /** Callback when the user chooses to go to the Workflows page */\n onGoToWorkflows: () => void;\n}\n\nconst STEPS = [\n 'Go to the Workflows page and create a new workflow.',\n 'Add a source node to fetch your data, then a node that writes to this data sink.',\n 'Run the workflow — the imported data appears here in the data preview.',\n];\n\nexport const ImportDataGuideDialog: FC<ImportDataGuideDialogProps> = memo(function ImportDataGuideDialog({\n isOpen,\n dataSinkName,\n onClose,\n onGoToWorkflows,\n}) {\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>\n <DialogContent className=\"sm:max-w-lg\">\n <DialogHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-action-primary-bg/10\">\n <Upload className=\"size-5 text-action-primary-bg\" />\n </div>\n <DialogTitle>Import data</DialogTitle>\n </div>\n <DialogDescription>\n {dataSinkName ? `Populate “${dataSinkName}” with a workflow` : 'Populate this data sink with a workflow'}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"py-2 space-y-4\">\n <p className=\"text-sm text-text-primary leading-relaxed\">\n Data is imported into a data sink by building a <strong>workflow</strong>. A workflow fetches data from your\n source (an API, database, file, or app) and writes it into this data sink so your dashboards can read it.\n </p>\n\n <ol className=\"space-y-3\">\n {STEPS.map((step, index) => (\n <li key={index} className=\"flex items-start gap-3\">\n <span className=\"flex size-6 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-xs font-semibold text-text-primary\">\n {index + 1}\n </span>\n <p className=\"text-sm text-text-secondary leading-relaxed\">{step}</p>\n </li>\n ))}\n </ol>\n </div>\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={onClose}>\n Cancel\n </Button>\n <Button onClick={onGoToWorkflows}>\n <Workflow className=\"size-4\" />\n Go to Workflows\n <ArrowRight className=\"size-4\" />\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n});\n\nexport default ImportDataGuideDialog;\n"],"mappings":";;;;;AA6BA,IAAM,IAAQ;CACZ;CACA;CACA;CACD,EAEY,IAAwD,EAAK,SAA+B,EACvG,WACA,iBACA,YACA,sBACC;AACD,QACE,kBAAC,GAAD;EAAQ,MAAM;EAAQ,eAAe,MAAS,CAAC,KAAQ,GAAS;YAC9D,kBAAC,GAAD;GAAe,WAAU;aAAzB;IACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD,EAAQ,WAAU,iCAAkC,CAAA;MAChD,CAAA,EACN,kBAAC,GAAD,EAAA,UAAa,eAAyB,CAAA,CAClC;QACN,kBAAC,GAAD,EAAA,UACG,IAAe,aAAa,EAAa,qBAAqB,2CAC7C,CAAA,CACP,EAAA,CAAA;IAEf,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAb;OAAyD;OACP,kBAAC,UAAD,EAAA,UAAQ,YAAiB,CAAA;;OAEvE;SAEJ,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAM,KAAK,GAAM,MAChB,kBAAC,MAAD;OAAgB,WAAU;iBAA1B,CACE,kBAAC,QAAD;QAAM,WAAU;kBACb,IAAQ;QACJ,CAAA,EACP,kBAAC,KAAD;QAAG,WAAU;kBAA+C;QAAS,CAAA,CAClE;SALI,EAKJ,CACL;MACC,CAAA,CACD;;IAEN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;KAAQ,SAAQ;KAAU,SAAS;eAAS;KAEnC,CAAA,EACT,kBAAC,GAAD;KAAQ,SAAS;eAAjB;MACE,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA;;MAE/B,kBAAC,GAAD,EAAY,WAAU,UAAW,CAAA;MAC1B;OACI,EAAA,CAAA;IACD;;EACT,CAAA;EAEX"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import e from "../../
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
1
|
+
import { useBuildPath as e } from "../../contexts/NavigationContext.js";
|
|
2
|
+
import "../../contexts/index.js";
|
|
3
|
+
import t from "../../hooks/useDataSinkOperations.js";
|
|
4
|
+
import n from "../datasink/DataSinkStatusBadge.js";
|
|
5
|
+
import { Fragment as r, memo as i, useCallback as a, useMemo as o, useState as s } from "react";
|
|
6
|
+
import { Plus as c } from "lucide-react";
|
|
7
|
+
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
5
8
|
//#region src/bigconsole/components/parser/DataSinkSelector.tsx
|
|
6
|
-
function
|
|
9
|
+
function d(e) {
|
|
7
10
|
if (!e) return "Never";
|
|
8
11
|
let t = new Date(e), n = Date.now() - t.getTime(), r = Math.floor(n / 6e4);
|
|
9
12
|
if (r < 1) return "Just now";
|
|
@@ -13,43 +16,51 @@ function l(e) {
|
|
|
13
16
|
let a = Math.floor(i / 24);
|
|
14
17
|
return a < 7 ? `${a}d ago` : t.toLocaleDateString();
|
|
15
18
|
}
|
|
16
|
-
var
|
|
19
|
+
var f = ({ className: e }) => /* @__PURE__ */ l("svg", {
|
|
17
20
|
className: e,
|
|
18
21
|
fill: "none",
|
|
19
22
|
stroke: "currentColor",
|
|
20
23
|
viewBox: "0 0 24 24",
|
|
21
|
-
children: /* @__PURE__ */
|
|
24
|
+
children: /* @__PURE__ */ l("path", {
|
|
22
25
|
strokeLinecap: "round",
|
|
23
26
|
strokeLinejoin: "round",
|
|
24
27
|
strokeWidth: 2,
|
|
25
28
|
d: "M19 9l-7 7-7-7"
|
|
26
29
|
})
|
|
27
|
-
}),
|
|
28
|
-
let [
|
|
29
|
-
if (
|
|
30
|
-
if (
|
|
31
|
-
let t =
|
|
30
|
+
}), p = i(function({ value: i, onChange: p, filterStatus: m, placeholder: h = "Select a data source...", disabled: g = !1, error: _ = !1, errorMessage: v }) {
|
|
31
|
+
let [y, b] = s(!1), [x, S] = s(""), C = e(), { dataSinks: w, loading: T, fetchDataSinks: E } = t({ skipFetch: !1 }), D = o(() => w.filter((e) => {
|
|
32
|
+
if (m && e.status !== m) return !1;
|
|
33
|
+
if (x) {
|
|
34
|
+
let t = x.toLowerCase();
|
|
32
35
|
return (e.name || e.key).toLowerCase().includes(t) || e.key.toLowerCase().includes(t) || e.description?.toLowerCase().includes(t);
|
|
33
36
|
}
|
|
34
37
|
return !0;
|
|
35
38
|
}), [
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
]),
|
|
40
|
-
|
|
41
|
-
}, [
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}, [
|
|
46
|
-
|
|
39
|
+
w,
|
|
40
|
+
m,
|
|
41
|
+
x
|
|
42
|
+
]), O = o(() => w.find((e) => e.key === i), [w, i]), k = a((e) => {
|
|
43
|
+
p(e), b(!1), S("");
|
|
44
|
+
}, [p]), A = a(() => {
|
|
45
|
+
if (g) return;
|
|
46
|
+
let e = !y;
|
|
47
|
+
e && E(), b(e);
|
|
48
|
+
}, [
|
|
49
|
+
g,
|
|
50
|
+
y,
|
|
51
|
+
E
|
|
52
|
+
]), j = a(() => {
|
|
53
|
+
b(!1);
|
|
54
|
+
}, []), M = a(() => {
|
|
55
|
+
b(!1), window.open(C("/datasinks/new"), "_blank", "noopener,noreferrer");
|
|
56
|
+
}, [C]);
|
|
57
|
+
return /* @__PURE__ */ u("div", {
|
|
47
58
|
className: "relative",
|
|
48
59
|
children: [
|
|
49
|
-
/* @__PURE__ */
|
|
60
|
+
/* @__PURE__ */ u("button", {
|
|
50
61
|
type: "button",
|
|
51
|
-
onClick:
|
|
52
|
-
disabled:
|
|
62
|
+
onClick: A,
|
|
63
|
+
disabled: g,
|
|
53
64
|
className: `
|
|
54
65
|
w-full
|
|
55
66
|
px-3 py-2
|
|
@@ -58,117 +69,129 @@ var u = ({ className: e }) => /* @__PURE__ */ s("svg", {
|
|
|
58
69
|
text-left
|
|
59
70
|
flex items-center justify-between gap-2
|
|
60
71
|
transition-colors
|
|
61
|
-
${
|
|
62
|
-
${
|
|
63
|
-
${
|
|
72
|
+
${g ? "opacity-50 cursor-not-allowed" : "hover:border-action-primary-bg cursor-pointer"}
|
|
73
|
+
${_ ? "border-status-error-border" : "border-border-default"}
|
|
74
|
+
${y ? "border-action-primary-bg ring-2 ring-action-primary-bg/20" : ""}
|
|
64
75
|
`,
|
|
65
|
-
children: [/* @__PURE__ */
|
|
76
|
+
children: [/* @__PURE__ */ l("div", {
|
|
66
77
|
className: "flex-1 min-w-0",
|
|
67
|
-
children:
|
|
78
|
+
children: O ? /* @__PURE__ */ u("div", {
|
|
68
79
|
className: "flex items-center gap-2",
|
|
69
|
-
children: [/* @__PURE__ */
|
|
70
|
-
status:
|
|
80
|
+
children: [/* @__PURE__ */ l(n, {
|
|
81
|
+
status: O.status,
|
|
71
82
|
size: "sm",
|
|
72
83
|
showIcon: !1
|
|
73
|
-
}), /* @__PURE__ */
|
|
84
|
+
}), /* @__PURE__ */ l("span", {
|
|
74
85
|
className: "text-sm text-text-primary truncate",
|
|
75
|
-
children:
|
|
86
|
+
children: O.name || O.key
|
|
76
87
|
})]
|
|
77
|
-
}) : /* @__PURE__ */
|
|
88
|
+
}) : /* @__PURE__ */ l("span", {
|
|
78
89
|
className: "text-sm text-text-tertiary",
|
|
79
|
-
children:
|
|
90
|
+
children: h
|
|
80
91
|
})
|
|
81
|
-
}), /* @__PURE__ */
|
|
92
|
+
}), /* @__PURE__ */ l(f, { className: `w-4 h-4 text-text-secondary transition-transform ${y ? "transform rotate-180" : ""}` })]
|
|
82
93
|
}),
|
|
83
|
-
|
|
94
|
+
_ && v && /* @__PURE__ */ l("p", {
|
|
84
95
|
className: "mt-1 text-xs text-status-error-text",
|
|
85
|
-
children:
|
|
96
|
+
children: v
|
|
86
97
|
}),
|
|
87
|
-
|
|
98
|
+
y && /* @__PURE__ */ u(r, { children: [/* @__PURE__ */ l("div", {
|
|
88
99
|
className: "fixed inset-0 z-10",
|
|
89
|
-
onClick:
|
|
90
|
-
}), /* @__PURE__ */
|
|
100
|
+
onClick: j
|
|
101
|
+
}), /* @__PURE__ */ u("div", {
|
|
91
102
|
className: "absolute left-0 right-0 top-full mt-1 z-20 bg-bg-surface border border-border-default rounded-md shadow-lg max-h-80 flex flex-col overflow-hidden",
|
|
92
|
-
children: [
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
className: "
|
|
105
|
-
children:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
103
|
+
children: [
|
|
104
|
+
/* @__PURE__ */ l("div", {
|
|
105
|
+
className: "p-2 border-b border-border-default",
|
|
106
|
+
children: /* @__PURE__ */ l("input", {
|
|
107
|
+
type: "text",
|
|
108
|
+
placeholder: "Search data sources...",
|
|
109
|
+
value: x,
|
|
110
|
+
onChange: (e) => S(e.target.value),
|
|
111
|
+
className: "\n w-full px-3 py-2\n bg-bg-sunken\n border border-border-default\n rounded-md\n text-sm text-text-primary\n placeholder-text-tertiary\n focus:outline-none\n focus:border-action-primary-bg\n focus:ring-2\n focus:ring-action-primary-bg/20\n "
|
|
112
|
+
})
|
|
113
|
+
}),
|
|
114
|
+
/* @__PURE__ */ l("div", {
|
|
115
|
+
className: "overflow-y-auto flex-1",
|
|
116
|
+
children: T ? /* @__PURE__ */ l("div", {
|
|
117
|
+
className: "p-4 text-center text-sm text-text-secondary",
|
|
118
|
+
children: "Loading data sources..."
|
|
119
|
+
}) : D.length === 0 ? /* @__PURE__ */ l("div", {
|
|
120
|
+
className: "p-4 text-center text-sm text-text-secondary",
|
|
121
|
+
children: x ? "No matching data sources found" : "No data sources available"
|
|
122
|
+
}) : /* @__PURE__ */ u(r, { children: [/* @__PURE__ */ l("button", {
|
|
123
|
+
type: "button",
|
|
124
|
+
onClick: () => k(null),
|
|
125
|
+
className: `
|
|
113
126
|
w-full px-3 py-2
|
|
114
127
|
text-left text-sm
|
|
115
128
|
hover:bg-bg-sunken
|
|
116
129
|
transition-colors
|
|
117
|
-
${
|
|
130
|
+
${i === null ? "bg-action-primary-bg/10 text-action-primary-bg" : "text-text-secondary"}
|
|
118
131
|
`,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
132
|
+
children: /* @__PURE__ */ l("span", {
|
|
133
|
+
className: "italic",
|
|
134
|
+
children: "None"
|
|
135
|
+
})
|
|
136
|
+
}), D.map((e) => /* @__PURE__ */ l("button", {
|
|
137
|
+
type: "button",
|
|
138
|
+
onClick: () => k(e.key),
|
|
139
|
+
className: `
|
|
127
140
|
w-full px-3 py-2.5
|
|
128
141
|
text-left
|
|
129
142
|
hover:bg-bg-sunken
|
|
130
143
|
transition-colors
|
|
131
144
|
border-b border-border-default last:border-b-0
|
|
132
|
-
${
|
|
145
|
+
${i === e.key ? "bg-action-primary-bg/10" : ""}
|
|
133
146
|
`,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
147
|
+
children: /* @__PURE__ */ l("div", {
|
|
148
|
+
className: "flex items-start justify-between gap-2",
|
|
149
|
+
children: /* @__PURE__ */ u("div", {
|
|
150
|
+
className: "flex-1 min-w-0",
|
|
151
|
+
children: [
|
|
152
|
+
/* @__PURE__ */ u("div", {
|
|
153
|
+
className: "flex items-center gap-2 mb-1",
|
|
154
|
+
children: [/* @__PURE__ */ l(n, {
|
|
155
|
+
status: e.status,
|
|
156
|
+
size: "sm",
|
|
157
|
+
showIcon: !1
|
|
158
|
+
}), /* @__PURE__ */ l("span", {
|
|
159
|
+
className: "text-sm font-medium text-text-primary truncate",
|
|
160
|
+
children: e.name || e.key
|
|
161
|
+
})]
|
|
162
|
+
}),
|
|
163
|
+
e.description && /* @__PURE__ */ l("p", {
|
|
164
|
+
className: "text-xs text-text-secondary line-clamp-1 mb-1",
|
|
165
|
+
children: e.description
|
|
166
|
+
}),
|
|
167
|
+
/* @__PURE__ */ u("div", {
|
|
168
|
+
className: "flex items-center gap-2 text-xs text-text-tertiary",
|
|
169
|
+
children: [
|
|
170
|
+
/* @__PURE__ */ u("span", { children: ["key: ", e.key] }),
|
|
171
|
+
/* @__PURE__ */ l("span", { children: "•" }),
|
|
172
|
+
/* @__PURE__ */ u("span", { children: ["Refreshed: ", d(e.lastRefreshedAt)] })
|
|
173
|
+
]
|
|
174
|
+
})
|
|
175
|
+
]
|
|
176
|
+
})
|
|
163
177
|
})
|
|
178
|
+
}, e.id))] })
|
|
179
|
+
}),
|
|
180
|
+
/* @__PURE__ */ l("div", {
|
|
181
|
+
className: "p-2 border-t border-border-default",
|
|
182
|
+
children: /* @__PURE__ */ u("button", {
|
|
183
|
+
type: "button",
|
|
184
|
+
onClick: M,
|
|
185
|
+
className: "\n w-full flex items-center gap-2\n px-3 py-2\n text-sm font-medium text-action-primary-bg\n rounded-md\n hover:bg-action-primary-bg/10\n transition-colors\n ",
|
|
186
|
+
children: [/* @__PURE__ */ l(c, { className: "w-4 h-4" }), "Create new data source"]
|
|
164
187
|
})
|
|
165
|
-
}
|
|
166
|
-
|
|
188
|
+
})
|
|
189
|
+
]
|
|
167
190
|
})] })
|
|
168
191
|
]
|
|
169
192
|
});
|
|
170
193
|
});
|
|
171
194
|
//#endregion
|
|
172
|
-
export {
|
|
195
|
+
export { p as default };
|
|
173
196
|
|
|
174
197
|
//# sourceMappingURL=DataSinkSelector.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataSinkSelector.js","names":[],"sources":["../../../../src/bigconsole/components/parser/DataSinkSelector.tsx"],"sourcesContent":["/**\n * DataSink Selector\n *\n * Dropdown component for selecting a DataSink as a parser's input source.\n */\n\nimport { memo, useState, useMemo, useCallback, Fragment } from 'react';\nimport { useDataSinkOperations } from '../../hooks/useDataSinkOperations';\nimport { DataSinkStatusBadge } from '../datasink/DataSinkStatusBadge';\nimport type { DataSinkStatus } from '../../types';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface DataSinkSelectorProps {\n /** Currently selected DataSink key */\n value: string | null;\n /** Callback when selection changes */\n onChange: (key: string | null) => void;\n /** Filter by status */\n filterStatus?: DataSinkStatus;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Whether to show error state */\n error?: boolean;\n /** Error message to display */\n errorMessage?: string;\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\n/**\n * Format relative time from date string\n */\nfunction formatRelativeTime(dateString: string | null | undefined): string {\n if (!dateString) return 'Never';\n\n const date = new Date(dateString);\n const diff = Date.now() - date.getTime();\n const minutes = Math.floor(diff / 60000);\n\n if (minutes < 1) return 'Just now';\n if (minutes < 60) return `${minutes}m ago`;\n\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n\n const days = Math.floor(hours / 24);\n if (days < 7) return `${days}d ago`;\n\n return date.toLocaleDateString();\n}\n\n// ============================================================================\n// ICONS\n// ============================================================================\n\nconst ChevronDownIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n);\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const DataSinkSelector = memo(function DataSinkSelector({\n value,\n onChange,\n filterStatus,\n placeholder = 'Select a data source...',\n disabled = false,\n error = false,\n errorMessage,\n}: DataSinkSelectorProps) {\n const [isOpen, setIsOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n\n const { dataSinks, loading } = useDataSinkOperations({ skipFetch: false });\n\n // Filter data sinks\n const filteredDataSinks = useMemo(\n () =>\n dataSinks.filter((ds) => {\n // Filter by status if specified\n if (filterStatus && ds.status !== filterStatus) return false;\n\n // Filter by search query\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n return (\n (ds.name || ds.key).toLowerCase().includes(query) ||\n ds.key.toLowerCase().includes(query) ||\n ds.description?.toLowerCase().includes(query)\n );\n }\n\n return true;\n }),\n [dataSinks, filterStatus, searchQuery]\n );\n\n // Find selected DataSink\n const selectedDataSink = useMemo(() => dataSinks.find((ds) => ds.key === value), [dataSinks, value]);\n\n // Handle selection\n const handleSelect = useCallback(\n (key: string | null) => {\n onChange(key);\n setIsOpen(false);\n setSearchQuery('');\n },\n [onChange]\n );\n\n // Toggle dropdown\n const handleToggle = useCallback(() => {\n if (!disabled) {\n setIsOpen((prev) => !prev);\n }\n }, [disabled]);\n\n // Close dropdown\n const handleClose = useCallback(() => {\n setIsOpen(false);\n }, []);\n\n return (\n <div className=\"relative\">\n {/* Trigger button */}\n <button\n type=\"button\"\n onClick={handleToggle}\n disabled={disabled}\n className={`\n w-full\n px-3 py-2\n bg-bg-surface\n border rounded-md\n text-left\n flex items-center justify-between gap-2\n transition-colors\n ${disabled ? 'opacity-50 cursor-not-allowed' : 'hover:border-action-primary-bg cursor-pointer'}\n ${error ? 'border-status-error-border' : 'border-border-default'}\n ${isOpen ? 'border-action-primary-bg ring-2 ring-action-primary-bg/20' : ''}\n `}\n >\n <div className=\"flex-1 min-w-0\">\n {selectedDataSink ? (\n <div className=\"flex items-center gap-2\">\n <DataSinkStatusBadge status={selectedDataSink.status} size=\"sm\" showIcon={false} />\n <span className=\"text-sm text-text-primary truncate\">\n {selectedDataSink.name || selectedDataSink.key}\n </span>\n </div>\n ) : (\n <span className=\"text-sm text-text-tertiary\">{placeholder}</span>\n )}\n </div>\n\n <ChevronDownIcon\n className={`w-4 h-4 text-text-secondary transition-transform ${isOpen ? 'transform rotate-180' : ''}`}\n />\n </button>\n\n {/* Error message */}\n {error && errorMessage && <p className=\"mt-1 text-xs text-status-error-text\">{errorMessage}</p>}\n\n {/* Dropdown */}\n {isOpen && (\n <Fragment>\n {/* Backdrop */}\n <div className=\"fixed inset-0 z-10\" onClick={handleClose} />\n\n {/* Dropdown content */}\n <div className=\"absolute left-0 right-0 top-full mt-1 z-20 bg-bg-surface border border-border-default rounded-md shadow-lg max-h-80 flex flex-col overflow-hidden\">\n {/* Search input */}\n <div className=\"p-2 border-b border-border-default\">\n <input\n type=\"text\"\n placeholder=\"Search data sources...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className={`\n w-full px-3 py-2\n bg-bg-sunken\n border border-border-default\n rounded-md\n text-sm text-text-primary\n placeholder-text-tertiary\n focus:outline-none\n focus:border-action-primary-bg\n focus:ring-2\n focus:ring-action-primary-bg/20\n `}\n />\n </div>\n\n {/* Options list */}\n <div className=\"overflow-y-auto flex-1\">\n {loading ? (\n <div className=\"p-4 text-center text-sm text-text-secondary\">Loading data sources...</div>\n ) : filteredDataSinks.length === 0 ? (\n <div className=\"p-4 text-center text-sm text-text-secondary\">\n {searchQuery ? 'No matching data sources found' : 'No data sources available'}\n </div>\n ) : (\n <Fragment>\n {/* None option */}\n <button\n type=\"button\"\n onClick={() => handleSelect(null)}\n className={`\n w-full px-3 py-2\n text-left text-sm\n hover:bg-bg-sunken\n transition-colors\n ${value === null ? 'bg-action-primary-bg/10 text-action-primary-bg' : 'text-text-secondary'}\n `}\n >\n <span className=\"italic\">None</span>\n </button>\n\n {/* DataSink options */}\n {filteredDataSinks.map((ds) => (\n <button\n key={ds.id}\n type=\"button\"\n onClick={() => handleSelect(ds.key)}\n className={`\n w-full px-3 py-2.5\n text-left\n hover:bg-bg-sunken\n transition-colors\n border-b border-border-default last:border-b-0\n ${value === ds.key ? 'bg-action-primary-bg/10' : ''}\n `}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex-1 min-w-0\">\n {/* Name and status */}\n <div className=\"flex items-center gap-2 mb-1\">\n <DataSinkStatusBadge status={ds.status} size=\"sm\" showIcon={false} />\n <span className=\"text-sm font-medium text-text-primary truncate\">{ds.name || ds.key}</span>\n </div>\n\n {/* Description */}\n {ds.description && (\n <p className=\"text-xs text-text-secondary line-clamp-1 mb-1\">{ds.description}</p>\n )}\n\n {/* Metadata */}\n <div className=\"flex items-center gap-2 text-xs text-text-tertiary\">\n <span>key: {ds.key}</span>\n <span>•</span>\n <span>Refreshed: {formatRelativeTime(ds.lastRefreshedAt)}</span>\n </div>\n </div>\n </div>\n </button>\n ))}\n </Fragment>\n )}\n </div>\n </div>\n </Fragment>\n )}\n </div>\n );\n});\n\nexport default DataSinkSelector;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DataSinkSelector.js","names":[],"sources":["../../../../src/bigconsole/components/parser/DataSinkSelector.tsx"],"sourcesContent":["/**\n * DataSink Selector\n *\n * Dropdown component for selecting a DataSink as a parser's input source.\n */\n\nimport { memo, useState, useMemo, useCallback, Fragment } from 'react';\nimport { Plus } from 'lucide-react';\nimport { useDataSinkOperations } from '../../hooks/useDataSinkOperations';\nimport { useBuildPath } from '../../contexts';\nimport { DataSinkStatusBadge } from '../datasink/DataSinkStatusBadge';\nimport type { DataSinkStatus } from '../../types';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface DataSinkSelectorProps {\n /** Currently selected DataSink key */\n value: string | null;\n /** Callback when selection changes */\n onChange: (key: string | null) => void;\n /** Filter by status */\n filterStatus?: DataSinkStatus;\n /** Placeholder text */\n placeholder?: string;\n /** Whether the selector is disabled */\n disabled?: boolean;\n /** Whether to show error state */\n error?: boolean;\n /** Error message to display */\n errorMessage?: string;\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\n/**\n * Format relative time from date string\n */\nfunction formatRelativeTime(dateString: string | null | undefined): string {\n if (!dateString) return 'Never';\n\n const date = new Date(dateString);\n const diff = Date.now() - date.getTime();\n const minutes = Math.floor(diff / 60000);\n\n if (minutes < 1) return 'Just now';\n if (minutes < 60) return `${minutes}m ago`;\n\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n\n const days = Math.floor(hours / 24);\n if (days < 7) return `${days}d ago`;\n\n return date.toLocaleDateString();\n}\n\n// ============================================================================\n// ICONS\n// ============================================================================\n\nconst ChevronDownIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n);\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const DataSinkSelector = memo(function DataSinkSelector({\n value,\n onChange,\n filterStatus,\n placeholder = 'Select a data source...',\n disabled = false,\n error = false,\n errorMessage,\n}: DataSinkSelectorProps) {\n const [isOpen, setIsOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n\n const buildPath = useBuildPath();\n\n const { dataSinks, loading, fetchDataSinks } = useDataSinkOperations({ skipFetch: false });\n\n // Filter data sinks\n const filteredDataSinks = useMemo(\n () =>\n dataSinks.filter((ds) => {\n // Filter by status if specified\n if (filterStatus && ds.status !== filterStatus) return false;\n\n // Filter by search query\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n return (\n (ds.name || ds.key).toLowerCase().includes(query) ||\n ds.key.toLowerCase().includes(query) ||\n ds.description?.toLowerCase().includes(query)\n );\n }\n\n return true;\n }),\n [dataSinks, filterStatus, searchQuery]\n );\n\n // Find selected DataSink\n const selectedDataSink = useMemo(() => dataSinks.find((ds) => ds.key === value), [dataSinks, value]);\n\n // Handle selection\n const handleSelect = useCallback(\n (key: string | null) => {\n onChange(key);\n setIsOpen(false);\n setSearchQuery('');\n },\n [onChange]\n );\n\n // Toggle dropdown\n const handleToggle = useCallback(() => {\n if (disabled) return;\n const next = !isOpen;\n // Refetch on open so a DataSink created via the \"Create new\" tab\n // shows up here without needing a full page reload.\n if (next) void fetchDataSinks();\n setIsOpen(next);\n }, [disabled, isOpen, fetchDataSinks]);\n\n // Close dropdown\n const handleClose = useCallback(() => {\n setIsOpen(false);\n }, []);\n\n // Open the create-DataSink page in a new tab so the current editing\n // context (e.g. an unsaved widget in the dashboard builder) is preserved.\n const handleCreateNew = useCallback(() => {\n setIsOpen(false);\n window.open(buildPath('/datasinks/new'), '_blank', 'noopener,noreferrer');\n }, [buildPath]);\n\n return (\n <div className=\"relative\">\n {/* Trigger button */}\n <button\n type=\"button\"\n onClick={handleToggle}\n disabled={disabled}\n className={`\n w-full\n px-3 py-2\n bg-bg-surface\n border rounded-md\n text-left\n flex items-center justify-between gap-2\n transition-colors\n ${disabled ? 'opacity-50 cursor-not-allowed' : 'hover:border-action-primary-bg cursor-pointer'}\n ${error ? 'border-status-error-border' : 'border-border-default'}\n ${isOpen ? 'border-action-primary-bg ring-2 ring-action-primary-bg/20' : ''}\n `}\n >\n <div className=\"flex-1 min-w-0\">\n {selectedDataSink ? (\n <div className=\"flex items-center gap-2\">\n <DataSinkStatusBadge status={selectedDataSink.status} size=\"sm\" showIcon={false} />\n <span className=\"text-sm text-text-primary truncate\">\n {selectedDataSink.name || selectedDataSink.key}\n </span>\n </div>\n ) : (\n <span className=\"text-sm text-text-tertiary\">{placeholder}</span>\n )}\n </div>\n\n <ChevronDownIcon\n className={`w-4 h-4 text-text-secondary transition-transform ${isOpen ? 'transform rotate-180' : ''}`}\n />\n </button>\n\n {/* Error message */}\n {error && errorMessage && <p className=\"mt-1 text-xs text-status-error-text\">{errorMessage}</p>}\n\n {/* Dropdown */}\n {isOpen && (\n <Fragment>\n {/* Backdrop */}\n <div className=\"fixed inset-0 z-10\" onClick={handleClose} />\n\n {/* Dropdown content */}\n <div className=\"absolute left-0 right-0 top-full mt-1 z-20 bg-bg-surface border border-border-default rounded-md shadow-lg max-h-80 flex flex-col overflow-hidden\">\n {/* Search input */}\n <div className=\"p-2 border-b border-border-default\">\n <input\n type=\"text\"\n placeholder=\"Search data sources...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className={`\n w-full px-3 py-2\n bg-bg-sunken\n border border-border-default\n rounded-md\n text-sm text-text-primary\n placeholder-text-tertiary\n focus:outline-none\n focus:border-action-primary-bg\n focus:ring-2\n focus:ring-action-primary-bg/20\n `}\n />\n </div>\n\n {/* Options list */}\n <div className=\"overflow-y-auto flex-1\">\n {loading ? (\n <div className=\"p-4 text-center text-sm text-text-secondary\">Loading data sources...</div>\n ) : filteredDataSinks.length === 0 ? (\n <div className=\"p-4 text-center text-sm text-text-secondary\">\n {searchQuery ? 'No matching data sources found' : 'No data sources available'}\n </div>\n ) : (\n <Fragment>\n {/* None option */}\n <button\n type=\"button\"\n onClick={() => handleSelect(null)}\n className={`\n w-full px-3 py-2\n text-left text-sm\n hover:bg-bg-sunken\n transition-colors\n ${value === null ? 'bg-action-primary-bg/10 text-action-primary-bg' : 'text-text-secondary'}\n `}\n >\n <span className=\"italic\">None</span>\n </button>\n\n {/* DataSink options */}\n {filteredDataSinks.map((ds) => (\n <button\n key={ds.id}\n type=\"button\"\n onClick={() => handleSelect(ds.key)}\n className={`\n w-full px-3 py-2.5\n text-left\n hover:bg-bg-sunken\n transition-colors\n border-b border-border-default last:border-b-0\n ${value === ds.key ? 'bg-action-primary-bg/10' : ''}\n `}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex-1 min-w-0\">\n {/* Name and status */}\n <div className=\"flex items-center gap-2 mb-1\">\n <DataSinkStatusBadge status={ds.status} size=\"sm\" showIcon={false} />\n <span className=\"text-sm font-medium text-text-primary truncate\">{ds.name || ds.key}</span>\n </div>\n\n {/* Description */}\n {ds.description && (\n <p className=\"text-xs text-text-secondary line-clamp-1 mb-1\">{ds.description}</p>\n )}\n\n {/* Metadata */}\n <div className=\"flex items-center gap-2 text-xs text-text-tertiary\">\n <span>key: {ds.key}</span>\n <span>•</span>\n <span>Refreshed: {formatRelativeTime(ds.lastRefreshedAt)}</span>\n </div>\n </div>\n </div>\n </button>\n ))}\n </Fragment>\n )}\n </div>\n\n {/* Create new — persistent footer action (opens in a new tab) */}\n <div className=\"p-2 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={handleCreateNew}\n className=\"\n w-full flex items-center gap-2\n px-3 py-2\n text-sm font-medium text-action-primary-bg\n rounded-md\n hover:bg-action-primary-bg/10\n transition-colors\n \"\n >\n <Plus className=\"w-4 h-4\" />\n Create new data source\n </button>\n </div>\n </div>\n </Fragment>\n )}\n </div>\n );\n});\n\nexport default DataSinkSelector;\n"],"mappings":"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"}
|