@burdenoff/microfe-bigconsole 2026.812.2 → 2026.825.1
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/customWidget/CustomWidgetBuilderSheet.js +44 -47
- package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js.map +1 -1
- package/dist/bigconsole/customWidget/proposalSource.js +31 -24
- package/dist/bigconsole/customWidget/proposalSource.js.map +1 -1
- package/dist/bigconsole/hooks/useDataSinkOperations.js +2 -2
- package/dist/bigconsole/hooks/useDataSinkOperations.js.map +1 -1
- package/package.json +1 -1
|
@@ -2,44 +2,41 @@ import e from "../hooks/useWidgetOperations.js";
|
|
|
2
2
|
import t from "../hooks/useDataSinkOperations.js";
|
|
3
3
|
import n from "../hooks/useParserOperations.js";
|
|
4
4
|
import "../hooks/index.js";
|
|
5
|
-
import r from "./
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
5
|
+
import { selectActiveProposalSinks as r } from "./proposalSource.js";
|
|
6
|
+
import i from "./CustomWidgetProposalPanel.js";
|
|
7
|
+
import { memo as a, useCallback as o, useMemo as s } from "react";
|
|
8
|
+
import { useI18n as c } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
9
|
+
import { Sheet as l, SheetContent as u, SheetDescription as d, SheetHeader as f, SheetTitle as p } from "@burdenoff/fe-libs/ui";
|
|
10
|
+
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
10
11
|
//#region src/bigconsole/customWidget/CustomWidgetBuilderSheet.tsx
|
|
11
|
-
var
|
|
12
|
+
var g = {
|
|
12
13
|
x: 0,
|
|
13
14
|
y: 0,
|
|
14
15
|
width: 6,
|
|
15
16
|
height: 4
|
|
16
|
-
},
|
|
17
|
-
let { t:
|
|
18
|
-
consoleId:
|
|
19
|
-
skipFetch: !
|
|
20
|
-
}), { parsers:
|
|
21
|
-
consoleId:
|
|
22
|
-
skipFetch: !
|
|
23
|
-
}),
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
name: e.name
|
|
27
|
-
})), [S]), k = o(() => {
|
|
28
|
-
let e = new Set(T.map((e) => e.id));
|
|
29
|
-
return [...T, ...D.filter((t) => !e.has(t.id))].map((e) => ({
|
|
17
|
+
}, _ = "default", v = a(function({ isOpen: a, onClose: v, dashboardId: y, pageId: b, source: x }) {
|
|
18
|
+
let { t: S } = c(), { dataSinks: C } = t({ skipFetch: !a }), { createWidget: w } = e(b, y, { skipFetch: !0 }), T = y === _, { parsers: E, executeParser: D } = n({
|
|
19
|
+
consoleId: y,
|
|
20
|
+
skipFetch: !a
|
|
21
|
+
}), { parsers: O } = n({
|
|
22
|
+
consoleId: _,
|
|
23
|
+
skipFetch: !a || T
|
|
24
|
+
}), k = s(() => r(C), [C]), A = s(() => {
|
|
25
|
+
let e = new Set(E.map((e) => e.id));
|
|
26
|
+
return [...E, ...O.filter((t) => !e.has(t.id))].map((e) => ({
|
|
30
27
|
id: e.id,
|
|
31
28
|
name: e.name,
|
|
32
29
|
inputSinkKey: e.inputSinkKey
|
|
33
30
|
}));
|
|
34
|
-
}, [
|
|
31
|
+
}, [E, O]), j = o(async (e) => {
|
|
35
32
|
if (!e.binding.parserId) return;
|
|
36
|
-
let t = await
|
|
37
|
-
if (!t || !t.success) throw Error(t?.error ||
|
|
33
|
+
let t = await D(e.binding.parserId);
|
|
34
|
+
if (!t || !t.success) throw Error(t?.error || S("bigconsole.widget.customWidget.error.couldNotResolveBinding", "Could not resolve the binding."));
|
|
38
35
|
return t.output;
|
|
39
|
-
}, [
|
|
40
|
-
if (!await
|
|
41
|
-
pageId:
|
|
42
|
-
dashboardId:
|
|
36
|
+
}, [D, S]), M = o(async (e) => {
|
|
37
|
+
if (!await w({
|
|
38
|
+
pageId: b,
|
|
39
|
+
dashboardId: y,
|
|
43
40
|
type: e.widgetType,
|
|
44
41
|
title: e.presentation.title,
|
|
45
42
|
description: e.presentation.description,
|
|
@@ -52,18 +49,18 @@ var h = {
|
|
|
52
49
|
sourcePrompt: e.sourcePrompt,
|
|
53
50
|
proposalVersion: e.version
|
|
54
51
|
},
|
|
55
|
-
position: { ...
|
|
56
|
-
})) throw Error(
|
|
52
|
+
position: { ...g }
|
|
53
|
+
})) throw Error(S("bigconsole.widget.customWidget.error.widgetNotCreated", "The widget could not be created."));
|
|
57
54
|
}, [
|
|
58
|
-
|
|
55
|
+
w,
|
|
56
|
+
b,
|
|
59
57
|
y,
|
|
60
|
-
|
|
61
|
-
x
|
|
58
|
+
S
|
|
62
59
|
]);
|
|
63
|
-
return /* @__PURE__ */
|
|
64
|
-
open:
|
|
65
|
-
onOpenChange: (e) => !e &&
|
|
66
|
-
children: /* @__PURE__ */
|
|
60
|
+
return /* @__PURE__ */ m(l, {
|
|
61
|
+
open: a,
|
|
62
|
+
onOpenChange: (e) => !e && v(),
|
|
63
|
+
children: /* @__PURE__ */ h(u, {
|
|
67
64
|
style: {
|
|
68
65
|
width: "min(480px, 100vw)",
|
|
69
66
|
maxWidth: "100vw",
|
|
@@ -71,32 +68,32 @@ var h = {
|
|
|
71
68
|
display: "flex",
|
|
72
69
|
flexDirection: "column"
|
|
73
70
|
},
|
|
74
|
-
children: [/* @__PURE__ */
|
|
71
|
+
children: [/* @__PURE__ */ h(f, {
|
|
75
72
|
style: {
|
|
76
73
|
padding: "var(--space-cardPadding)",
|
|
77
74
|
borderBottom: "1px solid var(--color-border-subtle)",
|
|
78
75
|
backgroundColor: "var(--color-bg-surface)",
|
|
79
76
|
flexShrink: 0
|
|
80
77
|
},
|
|
81
|
-
children: [/* @__PURE__ */ p
|
|
82
|
-
}), /* @__PURE__ */
|
|
78
|
+
children: [/* @__PURE__ */ m(p, { children: S("bigconsole.widget.customWidget.sheet.title", "AI custom widget") }), /* @__PURE__ */ m(d, { children: S("bigconsole.widget.customWidget.sheet.description", "Describe a widget, review the proposal, then approve it onto the dashboard.") })]
|
|
79
|
+
}), /* @__PURE__ */ m("div", {
|
|
83
80
|
style: {
|
|
84
81
|
flex: 1,
|
|
85
82
|
minHeight: 0
|
|
86
83
|
},
|
|
87
|
-
children: /* @__PURE__ */
|
|
88
|
-
sinks:
|
|
89
|
-
parsers:
|
|
90
|
-
resolvePreviewData:
|
|
91
|
-
onApprove:
|
|
92
|
-
onClose:
|
|
93
|
-
source:
|
|
84
|
+
children: /* @__PURE__ */ m(i, {
|
|
85
|
+
sinks: k,
|
|
86
|
+
parsers: A,
|
|
87
|
+
resolvePreviewData: j,
|
|
88
|
+
onApprove: M,
|
|
89
|
+
onClose: v,
|
|
90
|
+
source: x
|
|
94
91
|
})
|
|
95
92
|
})]
|
|
96
93
|
})
|
|
97
94
|
});
|
|
98
95
|
});
|
|
99
96
|
//#endregion
|
|
100
|
-
export {
|
|
97
|
+
export { v as default };
|
|
101
98
|
|
|
102
99
|
//# sourceMappingURL=CustomWidgetBuilderSheet.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CustomWidgetBuilderSheet.js","names":[],"sources":["../../../src/bigconsole/customWidget/CustomWidgetBuilderSheet.tsx"],"sourcesContent":["/**\n * Container that wires the custom-widget proposal panel to real workspace data\n * (BOFF-5532).\n *\n * Supplies the panel with the user's actual permitted DataSinks and parsers,\n * resolves previews by running the real parser, and persists an approved\n * proposal through the normal `createWidget` path — so an approved widget is an\n * ordinary widget, editable afterwards through the standard inspector.\n */\n\nimport { type FC, memo, useCallback, useMemo } from 'react';\nimport { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nimport { useDataSinkOperations, useParserOperations, useWidgetOperations } from '../hooks';\nimport { CustomWidgetProposalPanel } from './CustomWidgetProposalPanel';\nimport type { CustomWidgetProposal } from './proposalContract';\nimport type
|
|
1
|
+
{"version":3,"file":"CustomWidgetBuilderSheet.js","names":[],"sources":["../../../src/bigconsole/customWidget/CustomWidgetBuilderSheet.tsx"],"sourcesContent":["/**\n * Container that wires the custom-widget proposal panel to real workspace data\n * (BOFF-5532).\n *\n * Supplies the panel with the user's actual permitted DataSinks and parsers,\n * resolves previews by running the real parser, and persists an approved\n * proposal through the normal `createWidget` path — so an approved widget is an\n * ordinary widget, editable afterwards through the standard inspector.\n */\n\nimport { type FC, memo, useCallback, useMemo } from 'react';\nimport { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nimport { useDataSinkOperations, useParserOperations, useWidgetOperations } from '../hooks';\nimport { CustomWidgetProposalPanel } from './CustomWidgetProposalPanel';\nimport type { CustomWidgetProposal } from './proposalContract';\nimport {\n selectActiveProposalSinks,\n type CustomWidgetProposalSource,\n type ProposalCandidateParser,\n type ProposalCandidateSink,\n} from './proposalSource';\n\nexport interface CustomWidgetBuilderSheetProps {\n isOpen: boolean;\n onClose: () => void;\n dashboardId: string;\n pageId: string;\n /** Swap for the GraphQL-backed source once BOFF-3021's backend half lands. */\n source?: CustomWidgetProposalSource;\n}\n\n/** Grid slot for an approved widget; the reviewer can move it afterwards. */\nconst DEFAULT_POSITION = { x: 0, y: 0, width: 6, height: 4 } as const;\n\n/** Sentinel consoleId under which workspace-global parsers are stored. */\nconst GLOBAL_CONSOLE_ID = 'default';\n\nexport const CustomWidgetBuilderSheet: FC<CustomWidgetBuilderSheetProps> = memo(function CustomWidgetBuilderSheet({\n isOpen,\n onClose,\n dashboardId,\n pageId,\n source,\n}) {\n const { t } = useI18n();\n const { dataSinks } = useDataSinkOperations({ skipFetch: !isOpen });\n const { createWidget } = useWidgetOperations(pageId, dashboardId, { skipFetch: true });\n\n // Parsers are either dashboard-scoped or workspace-global (`consoleId: 'default'`),\n // and the widget inspector offers both. Fetch both here too, otherwise a proposal\n // can never bind to a global parser — which is what the parser builder creates by\n // default, so the picker would look empty in a typical workspace.\n const isGlobalConsole = dashboardId === GLOBAL_CONSOLE_ID;\n const { parsers: dashboardParsers, executeParser } = useParserOperations({\n consoleId: dashboardId,\n skipFetch: !isOpen,\n });\n const { parsers: globalParsers } = useParserOperations({\n consoleId: GLOBAL_CONSOLE_ID,\n skipFetch: !isOpen || isGlobalConsole,\n });\n\n const sinks: ProposalCandidateSink[] = useMemo(() => selectActiveProposalSinks(dataSinks), [dataSinks]);\n\n const parserCandidates: ProposalCandidateParser[] = useMemo(() => {\n const seen = new Set(dashboardParsers.map((parser) => parser.id));\n const merged = [...dashboardParsers, ...globalParsers.filter((parser) => !seen.has(parser.id))];\n return merged.map((parser) => ({ id: parser.id, name: parser.name, inputSinkKey: parser.inputSinkKey }));\n }, [dashboardParsers, globalParsers]);\n\n /**\n * Resolves what the binding actually produces. Rejects on failure so the\n * panel can distinguish \"no data\" from \"you cannot read this\".\n */\n const resolvePreviewData = useCallback(\n async (proposal: CustomWidgetProposal): Promise<unknown> => {\n if (!proposal.binding.parserId) return undefined;\n const execution = await executeParser(proposal.binding.parserId);\n if (!execution || !execution.success) {\n throw new Error(\n execution?.error ||\n t('bigconsole.widget.customWidget.error.couldNotResolveBinding', 'Could not resolve the binding.')\n );\n }\n return execution.output;\n },\n [executeParser, t]\n );\n\n const handleApprove = useCallback(\n async (proposal: CustomWidgetProposal) => {\n const widget = await createWidget({\n pageId,\n dashboardId,\n type: proposal.widgetType,\n title: proposal.presentation.title,\n description: proposal.presentation.description,\n dataSinkId: proposal.binding.dataSinkId || undefined,\n parserId: proposal.binding.parserId,\n config: {\n ...proposal.presentation.config,\n ...proposal.fieldMapping,\n // Provenance, so an approved widget can be traced back to its prompt\n // and survives export/import and clone with its origin intact.\n generatedBy: proposal.generatedBy,\n sourcePrompt: proposal.sourcePrompt,\n proposalVersion: proposal.version,\n },\n position: { ...DEFAULT_POSITION },\n });\n\n if (!widget)\n throw new Error(t('bigconsole.widget.customWidget.error.widgetNotCreated', 'The widget could not be created.'));\n },\n [createWidget, pageId, dashboardId, t]\n );\n\n return (\n <Sheet open={isOpen} onOpenChange={(open) => !open && onClose()}>\n {/*\n `min(480px, 100vw)` rather than a fixed 480px + 95vw cap, and header\n padding on the `--space-*` scale rather than raw pixels, so this sheet\n follows the same rules as the widget inspector (BOFF-5636).\n */}\n <SheetContent\n style={{\n width: 'min(480px, 100vw)',\n maxWidth: '100vw',\n padding: 0,\n display: 'flex',\n flexDirection: 'column',\n }}\n >\n <SheetHeader\n style={{\n padding: 'var(--space-cardPadding)',\n borderBottom: '1px solid var(--color-border-subtle)',\n backgroundColor: 'var(--color-bg-surface)',\n flexShrink: 0,\n }}\n >\n <SheetTitle>{t('bigconsole.widget.customWidget.sheet.title', 'AI custom widget')}</SheetTitle>\n <SheetDescription>\n {t(\n 'bigconsole.widget.customWidget.sheet.description',\n 'Describe a widget, review the proposal, then approve it onto the dashboard.'\n )}\n </SheetDescription>\n </SheetHeader>\n\n <div style={{ flex: 1, minHeight: 0 }}>\n <CustomWidgetProposalPanel\n sinks={sinks}\n parsers={parserCandidates}\n resolvePreviewData={resolvePreviewData}\n onApprove={handleApprove}\n onClose={onClose}\n source={source}\n />\n </div>\n </SheetContent>\n </Sheet>\n );\n});\n\nexport default CustomWidgetBuilderSheet;\n"],"mappings":";;;;;;;;;;;AAkCA,IAAM,IAAmB;CAAE,GAAG;CAAG,GAAG;CAAG,OAAO;CAAG,QAAQ;CAAG,EAGtD,IAAoB,WAEb,IAA8D,EAAK,SAAkC,EAChH,WACA,YACA,gBACA,WACA,aACC;CACD,IAAM,EAAE,SAAM,GAAS,EACjB,EAAE,iBAAc,EAAsB,EAAE,WAAW,CAAC,GAAQ,CAAC,EAC7D,EAAE,oBAAiB,EAAoB,GAAQ,GAAa,EAAE,WAAW,IAAM,CAAC,EAMhF,IAAkB,MAAgB,GAClC,EAAE,SAAS,GAAkB,qBAAkB,EAAoB;EACvE,WAAW;EACX,WAAW,CAAC;EACb,CAAC,EACI,EAAE,SAAS,MAAkB,EAAoB;EACrD,WAAW;EACX,WAAW,CAAC,KAAU;EACvB,CAAC,EAEI,IAAiC,QAAc,EAA0B,EAAU,EAAE,CAAC,EAAU,CAAC,EAEjG,IAA8C,QAAc;EAChE,IAAM,IAAO,IAAI,IAAI,EAAiB,KAAK,MAAW,EAAO,GAAG,CAAC;AAEjE,SADe,CAAC,GAAG,GAAkB,GAAG,EAAc,QAAQ,MAAW,CAAC,EAAK,IAAI,EAAO,GAAG,CAAC,CAAC,CACjF,KAAK,OAAY;GAAE,IAAI,EAAO;GAAI,MAAM,EAAO;GAAM,cAAc,EAAO;GAAc,EAAE;IACvG,CAAC,GAAkB,EAAc,CAAC,EAM/B,IAAqB,EACzB,OAAO,MAAqD;AAC1D,MAAI,CAAC,EAAS,QAAQ,SAAU;EAChC,IAAM,IAAY,MAAM,EAAc,EAAS,QAAQ,SAAS;AAChE,MAAI,CAAC,KAAa,CAAC,EAAU,QAC3B,OAAU,MACR,GAAW,SACT,EAAE,+DAA+D,iCAAiC,CACrG;AAEH,SAAO,EAAU;IAEnB,CAAC,GAAe,EAAE,CACnB,EAEK,IAAgB,EACpB,OAAO,MAAmC;AAqBxC,MAAI,CApBW,MAAM,EAAa;GAChC;GACA;GACA,MAAM,EAAS;GACf,OAAO,EAAS,aAAa;GAC7B,aAAa,EAAS,aAAa;GACnC,YAAY,EAAS,QAAQ,cAAc,KAAA;GAC3C,UAAU,EAAS,QAAQ;GAC3B,QAAQ;IACN,GAAG,EAAS,aAAa;IACzB,GAAG,EAAS;IAGZ,aAAa,EAAS;IACtB,cAAc,EAAS;IACvB,iBAAiB,EAAS;IAC3B;GACD,UAAU,EAAE,GAAG,GAAkB;GAClC,CAAC,CAGA,OAAU,MAAM,EAAE,yDAAyD,mCAAmC,CAAC;IAEnH;EAAC;EAAc;EAAQ;EAAa;EAAE,CACvC;AAED,QACE,kBAAC,GAAD;EAAO,MAAM;EAAQ,eAAe,MAAS,CAAC,KAAQ,GAAS;YAM7D,kBAAC,GAAD;GACE,OAAO;IACL,OAAO;IACP,UAAU;IACV,SAAS;IACT,SAAS;IACT,eAAe;IAChB;aAPH,CASE,kBAAC,GAAD;IACE,OAAO;KACL,SAAS;KACT,cAAc;KACd,iBAAiB;KACjB,YAAY;KACb;cANH,CAQE,kBAAC,GAAD,EAAA,UAAa,EAAE,8CAA8C,mBAAmB,EAAc,CAAA,EAC9F,kBAAC,GAAD,EAAA,UACG,EACC,oDACA,8EACD,EACgB,CAAA,CACP;OAEd,kBAAC,OAAD;IAAK,OAAO;KAAE,MAAM;KAAG,WAAW;KAAG;cACnC,kBAAC,GAAD;KACS;KACP,SAAS;KACW;KACpB,WAAW;KACF;KACD;KACR,CAAA;IACE,CAAA,CACO;;EACT,CAAA;EAEV"}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
//#region src/bigconsole/customWidget/proposalSource.ts
|
|
2
|
-
|
|
2
|
+
function e(e) {
|
|
3
|
+
return e.filter((e) => !e.deletedAt).map(({ id: e, key: t, name: n }) => ({
|
|
4
|
+
id: e,
|
|
5
|
+
key: t,
|
|
6
|
+
name: n
|
|
7
|
+
}));
|
|
8
|
+
}
|
|
9
|
+
var t = [
|
|
3
10
|
{
|
|
4
11
|
type: "funnel_chart",
|
|
5
12
|
words: [
|
|
@@ -58,7 +65,7 @@ var e = [
|
|
|
58
65
|
"graph"
|
|
59
66
|
]
|
|
60
67
|
}
|
|
61
|
-
],
|
|
68
|
+
], n = [
|
|
62
69
|
{
|
|
63
70
|
chartType: "bar",
|
|
64
71
|
words: ["bar"]
|
|
@@ -85,17 +92,17 @@ var e = [
|
|
|
85
92
|
]
|
|
86
93
|
}
|
|
87
94
|
];
|
|
88
|
-
function n(t) {
|
|
89
|
-
let n = t.toLowerCase();
|
|
90
|
-
for (let { type: t, words: r } of e) if (r.some((e) => n.includes(e))) return t;
|
|
91
|
-
return "chart";
|
|
92
|
-
}
|
|
93
95
|
function r(e) {
|
|
94
96
|
let n = e.toLowerCase();
|
|
95
|
-
for (let {
|
|
97
|
+
for (let { type: e, words: r } of t) if (r.some((e) => n.includes(e))) return e;
|
|
98
|
+
return "chart";
|
|
99
|
+
}
|
|
100
|
+
function i(e) {
|
|
101
|
+
let t = e.toLowerCase();
|
|
102
|
+
for (let { chartType: e, words: r } of n) if (r.some((e) => t.includes(e))) return e;
|
|
96
103
|
return "line";
|
|
97
104
|
}
|
|
98
|
-
function
|
|
105
|
+
function a(e, t) {
|
|
99
106
|
if (t.length === 0) return;
|
|
100
107
|
let n = e.toLowerCase().split(/[^a-z0-9]+/).filter((e) => e.length > 2), r;
|
|
101
108
|
for (let e of t) {
|
|
@@ -107,7 +114,7 @@ function i(e, t) {
|
|
|
107
114
|
}
|
|
108
115
|
return r?.sink ?? t[0];
|
|
109
116
|
}
|
|
110
|
-
function
|
|
117
|
+
function o(e) {
|
|
111
118
|
switch (e) {
|
|
112
119
|
case "heatmap": return {
|
|
113
120
|
xAxisField: "column",
|
|
@@ -131,7 +138,7 @@ function a(e) {
|
|
|
131
138
|
};
|
|
132
139
|
}
|
|
133
140
|
}
|
|
134
|
-
function
|
|
141
|
+
function s(e, t) {
|
|
135
142
|
let n = {
|
|
136
143
|
showLegend: !0,
|
|
137
144
|
showTooltip: !0
|
|
@@ -139,7 +146,7 @@ function o(e, t) {
|
|
|
139
146
|
switch (e) {
|
|
140
147
|
case "chart": return {
|
|
141
148
|
...n,
|
|
142
|
-
chartType:
|
|
149
|
+
chartType: i(t),
|
|
143
150
|
showGrid: !0
|
|
144
151
|
};
|
|
145
152
|
case "funnel_chart": return {
|
|
@@ -166,39 +173,39 @@ function o(e, t) {
|
|
|
166
173
|
default: return n;
|
|
167
174
|
}
|
|
168
175
|
}
|
|
169
|
-
function
|
|
176
|
+
function c(e) {
|
|
170
177
|
let t = e.trim().replace(/^(please\s+)?(create|build|make|add|show me|show|give me)\s+(an?\s+|the\s+)?/i, "").split(/[.;\n]/)[0].trim();
|
|
171
178
|
if (!t) return "Custom widget";
|
|
172
179
|
let n = t.charAt(0).toUpperCase() + t.slice(1);
|
|
173
180
|
return n.length > 60 ? `${n.slice(0, 57)}…` : n;
|
|
174
181
|
}
|
|
175
|
-
var
|
|
182
|
+
var l = 0, u = {
|
|
176
183
|
id: "local-heuristic-v1",
|
|
177
|
-
async propose({ prompt: e, sinks: t, parsers:
|
|
178
|
-
let
|
|
179
|
-
return
|
|
184
|
+
async propose({ prompt: e, sinks: t, parsers: n }) {
|
|
185
|
+
let i = r(e), d = a(e, t), f = d ? n.find((e) => e.inputSinkKey === d.key) : void 0;
|
|
186
|
+
return l += 1, {
|
|
180
187
|
version: "1.0",
|
|
181
|
-
id: `proposal-${
|
|
188
|
+
id: `proposal-${l}`,
|
|
182
189
|
sourcePrompt: e,
|
|
183
|
-
generatedBy:
|
|
184
|
-
widgetType:
|
|
190
|
+
generatedBy: u.id,
|
|
191
|
+
widgetType: i,
|
|
185
192
|
binding: {
|
|
186
193
|
dataSinkId: d?.id ?? "",
|
|
187
194
|
dataSinkKey: d?.key,
|
|
188
195
|
parserId: f?.id,
|
|
189
196
|
parserName: f?.name
|
|
190
197
|
},
|
|
191
|
-
fieldMapping:
|
|
198
|
+
fieldMapping: o(i),
|
|
192
199
|
presentation: {
|
|
193
|
-
title:
|
|
200
|
+
title: c(e),
|
|
194
201
|
description: `Generated from: "${e}"`,
|
|
195
|
-
config:
|
|
202
|
+
config: s(i, e)
|
|
196
203
|
},
|
|
197
204
|
rationale: d ? `Matched DataSink "${d.key}"${f ? ` and parser "${f.name}"` : " (no parser found for this sink)"}.` : "No DataSink is available in this workspace — create one before saving this widget."
|
|
198
205
|
};
|
|
199
206
|
}
|
|
200
207
|
};
|
|
201
208
|
//#endregion
|
|
202
|
-
export {
|
|
209
|
+
export { u as localProposalSource, e as selectActiveProposalSinks };
|
|
203
210
|
|
|
204
211
|
//# sourceMappingURL=proposalSource.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proposalSource.js","names":[],"sources":["../../../src/bigconsole/customWidget/proposalSource.ts"],"sourcesContent":["/**\n * Proposal source — the seam between the review UI and whatever produces\n * proposals (BOFF-5532 / BOFF-3021).\n *\n * The UI only ever talks to `CustomWidgetProposalSource`. Today the default\n * implementation derives a proposal locally from the prompt and the workspace's\n * real sinks and parsers, because the backend operation that will emit typed\n * proposals is owned by BOFF-3021's backend half and does not exist yet.\n *\n * When it lands, add a `graphqlProposalSource` here that calls it and swap the\n * value passed to the panel. Nothing in the review, edit, approve or persist\n * path changes: those already run against real sinks, real parsers and the real\n * `createBigConsoleWidget` mutation.\n */\n\nimport {\n CUSTOM_WIDGET_PROPOSAL_VERSION,\n type CustomWidgetProposal,\n type ProposableWidgetType,\n type ProposalFieldMapping,\n} from './proposalContract';\n\n/** A sink the current user may bind to, as the panel knows it. */\nexport interface ProposalCandidateSink {\n id: string;\n key: string;\n name?: string | null;\n}\n\n/** A parser the current user may bind to. */\nexport interface ProposalCandidateParser {\n id: string;\n name: string;\n inputSinkKey: string;\n}\n\nexport interface ProposalRequest {\n prompt: string;\n sinks: readonly ProposalCandidateSink[];\n parsers: readonly ProposalCandidateParser[];\n}\n\nexport interface CustomWidgetProposalSource {\n /** Stable id, surfaced in the UI so reviewers know what produced a proposal. */\n readonly id: string;\n propose(request: ProposalRequest): Promise<CustomWidgetProposal>;\n}\n\n// ============================================================================\n// Local heuristic source\n// ============================================================================\n\n/**\n * Keyword → widget type. Ordered most-specific first so \"conversion funnel\"\n * matches funnel before a generic \"chart\" would win.\n */\nconst TYPE_KEYWORDS: ReadonlyArray<{ type: ProposableWidgetType; words: readonly string[] }> = [\n { type: 'funnel_chart', words: ['funnel', 'conversion', 'drop-off', 'dropoff', 'stage'] },\n { type: 'heatmap', words: ['heatmap', 'heat map', 'matrix', 'intensity', 'by hour', 'density'] },\n { type: 'retention', words: ['retention', 'cohort'] },\n { type: 'table', words: ['table', 'list of', 'rows', 'breakdown table', 'grid'] },\n { type: 'metric_card', words: ['kpi', 'single number', 'metric card', 'scorecard', 'total'] },\n { type: 'chart', words: ['chart', 'trend', 'over time', 'line', 'bar', 'area', 'pie', 'graph'] },\n];\n\nconst CHART_SUBTYPE_KEYWORDS: ReadonlyArray<{ chartType: string; words: readonly string[] }> = [\n { chartType: 'bar', words: ['bar'] },\n { chartType: 'area', words: ['area', 'cumulative'] },\n { chartType: 'pie', words: ['pie', 'composition', 'share', 'mix'] },\n { chartType: 'line', words: ['line', 'trend', 'over time'] },\n];\n\nfunction detectWidgetType(prompt: string): ProposableWidgetType {\n const lower = prompt.toLowerCase();\n for (const { type, words } of TYPE_KEYWORDS) {\n if (words.some((word) => lower.includes(word))) return type;\n }\n return 'chart';\n}\n\nfunction detectChartSubtype(prompt: string): string {\n const lower = prompt.toLowerCase();\n for (const { chartType, words } of CHART_SUBTYPE_KEYWORDS) {\n if (words.some((word) => lower.includes(word))) return chartType;\n }\n return 'line';\n}\n\n/**\n * Picks the sink whose key/name shares the most word-stems with the prompt.\n * Falls back to the first permitted sink so a proposal is always reviewable\n * rather than failing outright.\n */\nfunction pickSink(prompt: string, sinks: readonly ProposalCandidateSink[]): ProposalCandidateSink | undefined {\n if (sinks.length === 0) return undefined;\n const tokens = prompt\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter((token) => token.length > 2);\n\n let best: { sink: ProposalCandidateSink; score: number } | undefined;\n for (const sink of sinks) {\n const haystack = `${sink.key} ${sink.name ?? ''}`.toLowerCase();\n const score = tokens.reduce((acc, token) => (haystack.includes(token) ? acc + 1 : acc), 0);\n if (!best || score > best.score) best = { sink, score };\n }\n return best?.sink ?? sinks[0];\n}\n\n/** Default field mapping per widget type, using the renderers' conventions. */\nfunction defaultFieldMapping(type: ProposableWidgetType): ProposalFieldMapping {\n switch (type) {\n case 'heatmap':\n return { xAxisField: 'column', yAxisField: 'row', valueField: 'value' };\n case 'funnel_chart':\n return { labelField: 'name', valueField: 'value' };\n case 'metric_card':\n return { valueField: 'value' };\n case 'table':\n return {};\n case 'retention':\n return { cohortField: 'cohort', valueField: 'value' };\n case 'chart':\n default:\n return { xAxisField: 'name', labelField: 'name', valueField: 'value' };\n }\n}\n\nfunction defaultConfig(type: ProposableWidgetType, prompt: string): Record<string, unknown> {\n const base: Record<string, unknown> = { showLegend: true, showTooltip: true };\n switch (type) {\n case 'chart':\n return { ...base, chartType: detectChartSubtype(prompt), showGrid: true };\n case 'funnel_chart':\n return { ...base, chartType: 'funnel', showLabels: true, showPercentages: true, orientation: 'vertical' };\n case 'heatmap':\n return { ...base, colorScheme: 'blue', showValues: true };\n case 'table':\n return { ...base, pageSize: 10, sortable: true };\n case 'metric_card':\n return { ...base, showTrend: true };\n default:\n return base;\n }\n}\n\n/** Turns the prompt's first clause into a widget title. */\nfunction deriveTitle(prompt: string): string {\n const cleaned = prompt\n .trim()\n .replace(/^(please\\s+)?(create|build|make|add|show me|show|give me)\\s+(an?\\s+|the\\s+)?/i, '')\n .split(/[.;\\n]/)[0]\n .trim();\n if (!cleaned) return 'Custom widget';\n const titled = cleaned.charAt(0).toUpperCase() + cleaned.slice(1);\n return titled.length > 60 ? `${titled.slice(0, 57)}…` : titled;\n}\n\nlet proposalCounter = 0;\n\n/**\n * Derives a proposal from the prompt and the workspace's real, permitted sinks\n * and parsers. Deterministic, so the review UI and its tests behave predictably.\n */\nexport const localProposalSource: CustomWidgetProposalSource = {\n id: 'local-heuristic-v1',\n\n async propose({ prompt, sinks, parsers }: ProposalRequest): Promise<CustomWidgetProposal> {\n const widgetType = detectWidgetType(prompt);\n const sink = pickSink(prompt, sinks);\n const parser = sink ? parsers.find((candidate) => candidate.inputSinkKey === sink.key) : undefined;\n\n proposalCounter += 1;\n\n return {\n version: CUSTOM_WIDGET_PROPOSAL_VERSION,\n id: `proposal-${proposalCounter}`,\n sourcePrompt: prompt,\n generatedBy: localProposalSource.id,\n widgetType,\n binding: {\n dataSinkId: sink?.id ?? '',\n dataSinkKey: sink?.key,\n parserId: parser?.id,\n parserName: parser?.name,\n },\n fieldMapping: defaultFieldMapping(widgetType),\n presentation: {\n title: deriveTitle(prompt),\n description: `Generated from: \"${prompt}\"`,\n config: defaultConfig(widgetType, prompt),\n },\n rationale: sink\n ? `Matched DataSink \"${sink.key}\"${parser ? ` and parser \"${parser.name}\"` : ' (no parser found for this sink)'}.`\n : 'No DataSink is available in this workspace — create one before saving this widget.',\n };\n },\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"proposalSource.js","names":[],"sources":["../../../src/bigconsole/customWidget/proposalSource.ts"],"sourcesContent":["/**\n * Proposal source — the seam between the review UI and whatever produces\n * proposals (BOFF-5532 / BOFF-3021).\n *\n * The UI only ever talks to `CustomWidgetProposalSource`. Today the default\n * implementation derives a proposal locally from the prompt and the workspace's\n * real sinks and parsers, because the backend operation that will emit typed\n * proposals is owned by BOFF-3021's backend half and does not exist yet.\n *\n * When it lands, add a `graphqlProposalSource` here that calls it and swap the\n * value passed to the panel. Nothing in the review, edit, approve or persist\n * path changes: those already run against real sinks, real parsers and the real\n * `createBigConsoleWidget` mutation.\n */\n\nimport {\n CUSTOM_WIDGET_PROPOSAL_VERSION,\n type CustomWidgetProposal,\n type ProposableWidgetType,\n type ProposalFieldMapping,\n} from './proposalContract';\n\n/** A sink the current user may bind to, as the panel knows it. */\nexport interface ProposalCandidateSink {\n id: string;\n key: string;\n name?: string | null;\n}\n\n/**\n * Converts the workspace store into a proposal-safe candidate set. The API\n * excludes soft-deleted sinks, but this second boundary is intentional:\n * persisted client state can outlive an out-of-band deletion.\n */\nexport function selectActiveProposalSinks(\n sinks: ReadonlyArray<ProposalCandidateSink & { deletedAt?: string | null }>\n): ProposalCandidateSink[] {\n return sinks.filter((sink) => !sink.deletedAt).map(({ id, key, name }) => ({ id, key, name }));\n}\n\n/** A parser the current user may bind to. */\nexport interface ProposalCandidateParser {\n id: string;\n name: string;\n inputSinkKey: string;\n}\n\nexport interface ProposalRequest {\n prompt: string;\n sinks: readonly ProposalCandidateSink[];\n parsers: readonly ProposalCandidateParser[];\n}\n\nexport interface CustomWidgetProposalSource {\n /** Stable id, surfaced in the UI so reviewers know what produced a proposal. */\n readonly id: string;\n propose(request: ProposalRequest): Promise<CustomWidgetProposal>;\n}\n\n// ============================================================================\n// Local heuristic source\n// ============================================================================\n\n/**\n * Keyword → widget type. Ordered most-specific first so \"conversion funnel\"\n * matches funnel before a generic \"chart\" would win.\n */\nconst TYPE_KEYWORDS: ReadonlyArray<{ type: ProposableWidgetType; words: readonly string[] }> = [\n { type: 'funnel_chart', words: ['funnel', 'conversion', 'drop-off', 'dropoff', 'stage'] },\n { type: 'heatmap', words: ['heatmap', 'heat map', 'matrix', 'intensity', 'by hour', 'density'] },\n { type: 'retention', words: ['retention', 'cohort'] },\n { type: 'table', words: ['table', 'list of', 'rows', 'breakdown table', 'grid'] },\n { type: 'metric_card', words: ['kpi', 'single number', 'metric card', 'scorecard', 'total'] },\n { type: 'chart', words: ['chart', 'trend', 'over time', 'line', 'bar', 'area', 'pie', 'graph'] },\n];\n\nconst CHART_SUBTYPE_KEYWORDS: ReadonlyArray<{ chartType: string; words: readonly string[] }> = [\n { chartType: 'bar', words: ['bar'] },\n { chartType: 'area', words: ['area', 'cumulative'] },\n { chartType: 'pie', words: ['pie', 'composition', 'share', 'mix'] },\n { chartType: 'line', words: ['line', 'trend', 'over time'] },\n];\n\nfunction detectWidgetType(prompt: string): ProposableWidgetType {\n const lower = prompt.toLowerCase();\n for (const { type, words } of TYPE_KEYWORDS) {\n if (words.some((word) => lower.includes(word))) return type;\n }\n return 'chart';\n}\n\nfunction detectChartSubtype(prompt: string): string {\n const lower = prompt.toLowerCase();\n for (const { chartType, words } of CHART_SUBTYPE_KEYWORDS) {\n if (words.some((word) => lower.includes(word))) return chartType;\n }\n return 'line';\n}\n\n/**\n * Picks the sink whose key/name shares the most word-stems with the prompt.\n * Falls back to the first permitted sink so a proposal is always reviewable\n * rather than failing outright.\n */\nfunction pickSink(prompt: string, sinks: readonly ProposalCandidateSink[]): ProposalCandidateSink | undefined {\n if (sinks.length === 0) return undefined;\n const tokens = prompt\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter((token) => token.length > 2);\n\n let best: { sink: ProposalCandidateSink; score: number } | undefined;\n for (const sink of sinks) {\n const haystack = `${sink.key} ${sink.name ?? ''}`.toLowerCase();\n const score = tokens.reduce((acc, token) => (haystack.includes(token) ? acc + 1 : acc), 0);\n if (!best || score > best.score) best = { sink, score };\n }\n return best?.sink ?? sinks[0];\n}\n\n/** Default field mapping per widget type, using the renderers' conventions. */\nfunction defaultFieldMapping(type: ProposableWidgetType): ProposalFieldMapping {\n switch (type) {\n case 'heatmap':\n return { xAxisField: 'column', yAxisField: 'row', valueField: 'value' };\n case 'funnel_chart':\n return { labelField: 'name', valueField: 'value' };\n case 'metric_card':\n return { valueField: 'value' };\n case 'table':\n return {};\n case 'retention':\n return { cohortField: 'cohort', valueField: 'value' };\n case 'chart':\n default:\n return { xAxisField: 'name', labelField: 'name', valueField: 'value' };\n }\n}\n\nfunction defaultConfig(type: ProposableWidgetType, prompt: string): Record<string, unknown> {\n const base: Record<string, unknown> = { showLegend: true, showTooltip: true };\n switch (type) {\n case 'chart':\n return { ...base, chartType: detectChartSubtype(prompt), showGrid: true };\n case 'funnel_chart':\n return { ...base, chartType: 'funnel', showLabels: true, showPercentages: true, orientation: 'vertical' };\n case 'heatmap':\n return { ...base, colorScheme: 'blue', showValues: true };\n case 'table':\n return { ...base, pageSize: 10, sortable: true };\n case 'metric_card':\n return { ...base, showTrend: true };\n default:\n return base;\n }\n}\n\n/** Turns the prompt's first clause into a widget title. */\nfunction deriveTitle(prompt: string): string {\n const cleaned = prompt\n .trim()\n .replace(/^(please\\s+)?(create|build|make|add|show me|show|give me)\\s+(an?\\s+|the\\s+)?/i, '')\n .split(/[.;\\n]/)[0]\n .trim();\n if (!cleaned) return 'Custom widget';\n const titled = cleaned.charAt(0).toUpperCase() + cleaned.slice(1);\n return titled.length > 60 ? `${titled.slice(0, 57)}…` : titled;\n}\n\nlet proposalCounter = 0;\n\n/**\n * Derives a proposal from the prompt and the workspace's real, permitted sinks\n * and parsers. Deterministic, so the review UI and its tests behave predictably.\n */\nexport const localProposalSource: CustomWidgetProposalSource = {\n id: 'local-heuristic-v1',\n\n async propose({ prompt, sinks, parsers }: ProposalRequest): Promise<CustomWidgetProposal> {\n const widgetType = detectWidgetType(prompt);\n const sink = pickSink(prompt, sinks);\n const parser = sink ? parsers.find((candidate) => candidate.inputSinkKey === sink.key) : undefined;\n\n proposalCounter += 1;\n\n return {\n version: CUSTOM_WIDGET_PROPOSAL_VERSION,\n id: `proposal-${proposalCounter}`,\n sourcePrompt: prompt,\n generatedBy: localProposalSource.id,\n widgetType,\n binding: {\n dataSinkId: sink?.id ?? '',\n dataSinkKey: sink?.key,\n parserId: parser?.id,\n parserName: parser?.name,\n },\n fieldMapping: defaultFieldMapping(widgetType),\n presentation: {\n title: deriveTitle(prompt),\n description: `Generated from: \"${prompt}\"`,\n config: defaultConfig(widgetType, prompt),\n },\n rationale: sink\n ? `Matched DataSink \"${sink.key}\"${parser ? ` and parser \"${parser.name}\"` : ' (no parser found for this sink)'}.`\n : 'No DataSink is available in this workspace — create one before saving this widget.',\n };\n },\n};\n"],"mappings":"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"}
|
|
@@ -49,7 +49,7 @@ function g(e) {
|
|
|
49
49
|
function _(_ = {}) {
|
|
50
50
|
let { workspaceId: v, skipFetch: y = !1 } = _, [b, x] = m(!1), [S, C] = m(null), w = h(), T = e((e) => e.dataSinks), E = e((e) => e.setDataSinks), D = e((e) => e.addDataSink), O = e((e) => e.updateDataSink), k = e((e) => e.removeDataSink), A = e((e) => e.setDataSinkRefreshing), j = p(() => Array.from(T.values()), [T]), M = d(async (e) => {
|
|
51
51
|
try {
|
|
52
|
-
x(!0), C(null);
|
|
52
|
+
x(!0), C(null), E([]);
|
|
53
53
|
let t = ((await w.query({
|
|
54
54
|
query: c,
|
|
55
55
|
variables: {
|
|
@@ -61,7 +61,7 @@ function _(_ = {}) {
|
|
|
61
61
|
})).data?.listDataSinks?.edges || []).map((e) => g(e.node));
|
|
62
62
|
return E(t), t;
|
|
63
63
|
} catch (e) {
|
|
64
|
-
return C(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to fetch DataSinks")), [];
|
|
64
|
+
return C(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to fetch DataSinks")), E([]), [];
|
|
65
65
|
} finally {
|
|
66
66
|
x(!1);
|
|
67
67
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDataSinkOperations.js","names":[],"sources":["../../../src/bigconsole/hooks/useDataSinkOperations.ts"],"sourcesContent":["/**\n * DataSink Operations Hook\n *\n * Custom hook for DataSink CRUD operations with Apollo Client.\n * Integrates with the DataSink Zustand store for state management.\n */\n\nimport { useState, useMemo, useCallback, useEffect } from 'react';\nimport { useApolloClient } from '@apollo/client/react';\nimport { useDataSinkStore } from '../store/dataSinkStore';\nimport {\n ListDataSinksDocument,\n GetDataSinkDocument,\n GetDataSinkDataDocument,\n CreateDataSinkDocument,\n UpdateDataSinkDocument,\n DeleteDataSinkDocument,\n RefreshDataSinkDocument,\n InvalidateDataSinkCacheDocument,\n BatchCreateDataSinksDocument,\n BatchDeleteDataSinksDocument,\n} from '../../generated/wspace-operations';\nimport type { DataSink, DataSinkStatus, CreateDataSinkInput, UpdateDataSinkInput } from '../types';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface UseDataSinkOperationsOptions {\n workspaceId?: string;\n skipFetch?: boolean;\n}\n\ninterface DataSinkFilters {\n status?: DataSinkStatus;\n isActive?: boolean;\n search?: string;\n}\n\ninterface RefreshDataSinkInput {\n id: string;\n forceRefresh?: boolean;\n}\n\ninterface InvalidateCacheResult {\n count: number;\n invalidatedIds: string[];\n success: boolean;\n message?: string;\n}\n\ninterface DataSinkDataResult {\n raw: unknown;\n extracted: unknown;\n cached: boolean;\n version: number;\n fetchedAt: string;\n // v3.0: pagination metadata (non-null totalCount only for array payloads).\n totalCount?: number | null;\n limit?: number | null;\n offset?: number | null;\n hasMore?: boolean | null;\n}\n\n// GraphQL Query/Mutation Result Types\ninterface ListDataSinksResult {\n listDataSinks?: {\n edges: Array<{ node: unknown }>;\n pageInfo?: { hasNextPage: boolean; endCursor?: string };\n };\n}\n\ninterface GetDataSinkResult {\n getDataSink?: unknown;\n}\n\ninterface GetDataSinkDataResult {\n getDataSinkData?: DataSinkDataResult;\n}\n\ninterface CreateDataSinkResult {\n createDataSink?: unknown;\n}\n\ninterface UpdateDataSinkResult {\n updateDataSink?: unknown;\n}\n\ninterface DeleteDataSinkResult {\n deleteDataSink?: boolean;\n}\n\ninterface RefreshDataSinkResult {\n refreshDataSink?: unknown;\n}\n\ninterface InvalidateDataSinkCacheResult {\n invalidateDataSinkCache?: InvalidateCacheResult;\n}\n\ninterface BatchCreateDataSinksResult {\n batchCreateDataSinks?: unknown[];\n}\n\ninterface BatchDeleteDataSinksResult {\n batchDeleteDataSinks?: Array<{ id: string; deletedAt: string }>;\n}\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Normalize GraphQL response to DataSink type\n */\nfunction normalizeDataSink(data: unknown): DataSink {\n const raw = data as Record<string, unknown>;\n return {\n id: raw.id as string,\n workspaceId: raw.workspaceId as string,\n tenantId: raw.tenantId as string,\n name: (raw.name as string) || null,\n description: (raw.description as string) || null,\n key: raw.key as string,\n fluidGridsWorkflowId: (raw.fluidGridsWorkflowId as string) || null,\n fluidGridsWorkflowExecutionId: (raw.fluidGridsWorkflowExecutionId as string) || null,\n config: (raw.config as Record<string, unknown>) || null,\n data: (raw.data as Record<string, unknown>) || {},\n dataStats: (raw.dataStats as DataSink['dataStats']) || null,\n lastRefreshedAt: (raw.lastRefreshedAt as string) || new Date().toISOString(),\n lastRefreshedBy: (raw.lastRefreshedBy as string) || null,\n expiresAt: (raw.expiresAt as string) || null,\n nextRefreshAt: (raw.nextRefreshAt as string) || null,\n ttl: (raw.ttl as number) ?? 0,\n version: (raw.version as number) ?? 1,\n sizeBytes: (raw.sizeBytes as number) ?? 0,\n isActive: (raw.isActive as boolean) ?? true,\n fieldMaskingEnabled: (raw.fieldMaskingEnabled as boolean) ?? false,\n fieldMaskingRules: (raw.fieldMaskingRules as Record<string, unknown>) || null,\n status: (raw.status as DataSink['status']) || 'ready',\n statusChangedAt: (raw.statusChangedAt as string) || null,\n statusChangedBy: (raw.statusChangedBy as string) || null,\n setupState: (raw.setupState as DataSink['setupState']) || 'not_started',\n refresh: (raw.refresh as DataSink['refresh']) || null,\n exports: (raw.exports as DataSink['exports']) || null,\n alerts: (raw.alerts as DataSink['alerts']) || null,\n quota: (raw.quota as DataSink['quota']) || null,\n workflowLinks: raw.workflowLinks as DataSink['workflowLinks'],\n linkedWorkflowCount: raw.linkedWorkflowCount as number | undefined,\n metadata: (raw.metadata as Record<string, unknown>) || null,\n createdAt: raw.createdAt as string,\n createdBy: (raw.createdBy as string) || '',\n updatedAt: raw.updatedAt as string,\n updatedBy: (raw.updatedBy as string) || '',\n deletedAt: (raw.deletedAt as string) || null,\n deletedBy: (raw.deletedBy as string) || null,\n };\n}\n\n// ============================================================================\n// HOOK\n// ============================================================================\n\nexport function useDataSinkOperations(options: UseDataSinkOperationsOptions = {}) {\n const { workspaceId, skipFetch = false } = options;\n\n // Local state\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Apollo client\n const client = useApolloClient();\n\n // Store selectors and actions\n const dataSinksMap = useDataSinkStore((state) => state.dataSinks);\n const setDataSinks = useDataSinkStore((state) => state.setDataSinks);\n const addDataSink = useDataSinkStore((state) => state.addDataSink);\n const updateDataSinkInStore = useDataSinkStore((state) => state.updateDataSink);\n const removeDataSink = useDataSinkStore((state) => state.removeDataSink);\n const setDataSinkRefreshing = useDataSinkStore((state) => state.setDataSinkRefreshing);\n\n // Memoized array of datasinks\n const dataSinks = useMemo(() => Array.from(dataSinksMap.values()), [dataSinksMap]);\n\n // ============================================================================\n // OPERATIONS\n // ============================================================================\n\n /**\n * Fetch all datasinks with optional filters\n */\n const fetchDataSinks = useCallback(\n async (filters?: DataSinkFilters): Promise<DataSink[]> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.query<ListDataSinksResult>({\n query: ListDataSinksDocument,\n variables: {\n status: filters?.status,\n isActive: filters?.isActive,\n search: filters?.search,\n },\n fetchPolicy: 'network-only',\n });\n\n const edges = result.data?.listDataSinks?.edges || [];\n const normalized = edges.map((edge: { node: unknown }) => normalizeDataSink(edge.node));\n\n setDataSinks(normalized);\n return normalized;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to fetch DataSinks');\n setError(error);\n return [];\n } finally {\n setLoading(false);\n }\n },\n [client, setDataSinks]\n );\n\n /**\n * Fetch a single datasink by ID\n */\n const fetchDataSink = useCallback(\n async (id: string): Promise<DataSink | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.query<GetDataSinkResult>({\n query: GetDataSinkDocument,\n variables: { id },\n fetchPolicy: 'network-only',\n });\n\n if (result.data?.getDataSink) {\n const normalized = normalizeDataSink(result.data.getDataSink);\n addDataSink(normalized);\n return normalized;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to fetch DataSink');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client, addDataSink]\n );\n\n /**\n * Get datasink data for preview\n */\n const getDataSinkData = useCallback(\n async (\n idOrKey: { id?: string; key?: string },\n options?: {\n dataPath?: string;\n forceRefresh?: boolean;\n // v3.0: server-side pagination + filter for array payloads.\n limit?: number;\n offset?: number;\n filter?: Array<{ field: string; op: string; value: unknown }>;\n }\n ): Promise<DataSinkDataResult | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.query<GetDataSinkDataResult>({\n query: GetDataSinkDataDocument,\n variables: {\n id: idOrKey.id,\n key: idOrKey.key,\n dataPath: options?.dataPath,\n forceRefresh: options?.forceRefresh,\n limit: options?.limit,\n offset: options?.offset,\n filter: options?.filter,\n },\n fetchPolicy: 'network-only',\n });\n\n return result.data?.getDataSinkData || null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to get DataSink data');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client]\n );\n\n /**\n * Create a new datasink\n */\n const createDataSink = useCallback(\n async (input: CreateDataSinkInput): Promise<DataSink | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<CreateDataSinkResult>({\n mutation: CreateDataSinkDocument,\n variables: { input },\n });\n\n if (result.data?.createDataSink) {\n const normalized = normalizeDataSink(result.data.createDataSink);\n addDataSink(normalized);\n return normalized;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to create DataSink');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client, addDataSink]\n );\n\n /**\n * Update an existing datasink\n */\n const updateDataSink = useCallback(\n async (input: UpdateDataSinkInput): Promise<DataSink | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<UpdateDataSinkResult>({\n mutation: UpdateDataSinkDocument,\n variables: { input },\n });\n\n if (result.data?.updateDataSink) {\n const normalized = normalizeDataSink(result.data.updateDataSink);\n updateDataSinkInStore(normalized.id, normalized);\n return normalized;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to update DataSink');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client, updateDataSinkInStore]\n );\n\n /**\n * Delete a datasink\n */\n const deleteDataSink = useCallback(\n async (id: string): Promise<boolean> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<DeleteDataSinkResult>({\n mutation: DeleteDataSinkDocument,\n variables: { id },\n });\n\n if (result.data?.deleteDataSink) {\n removeDataSink(id);\n return true;\n }\n\n return false;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to delete DataSink');\n setError(error);\n return false;\n } finally {\n setLoading(false);\n }\n },\n [client, removeDataSink]\n );\n\n /**\n * Refresh a datasink's data\n */\n const refreshDataSink = useCallback(\n async (input: RefreshDataSinkInput): Promise<DataSink | null> => {\n try {\n setLoading(true);\n setError(null);\n\n if (input.id) {\n setDataSinkRefreshing(input.id, true);\n }\n\n const result = await client.mutate<RefreshDataSinkResult>({\n mutation: RefreshDataSinkDocument,\n variables: { input },\n });\n\n if (result.data?.refreshDataSink) {\n const normalized = normalizeDataSink(result.data.refreshDataSink);\n updateDataSinkInStore(normalized.id, normalized);\n setDataSinkRefreshing(normalized.id, false);\n return normalized;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to refresh DataSink');\n setError(error);\n if (input.id) {\n setDataSinkRefreshing(input.id, false);\n }\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client, updateDataSinkInStore, setDataSinkRefreshing]\n );\n\n /**\n * Invalidate datasink cache\n */\n const invalidateCache = useCallback(\n async (id?: string): Promise<InvalidateCacheResult> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<InvalidateDataSinkCacheResult>({\n mutation: InvalidateDataSinkCacheDocument,\n variables: {\n id,\n },\n });\n\n return (\n result.data?.invalidateDataSinkCache || {\n count: 0,\n invalidatedIds: [],\n success: false,\n }\n );\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to invalidate cache');\n setError(error);\n return {\n count: 0,\n invalidatedIds: [],\n success: false,\n message: error.message,\n };\n } finally {\n setLoading(false);\n }\n },\n [client, workspaceId]\n );\n\n /**\n * Batch create datasinks\n */\n const batchCreateDataSinks = useCallback(\n async (inputs: CreateDataSinkInput[]): Promise<DataSink[]> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<BatchCreateDataSinksResult>({\n mutation: BatchCreateDataSinksDocument,\n variables: { inputs },\n });\n\n if (result.data?.batchCreateDataSinks) {\n const normalized = result.data.batchCreateDataSinks.map(normalizeDataSink);\n normalized.forEach(addDataSink);\n return normalized;\n }\n\n return [];\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to batch create DataSinks');\n setError(error);\n return [];\n } finally {\n setLoading(false);\n }\n },\n [client, addDataSink]\n );\n\n /**\n * Batch delete datasinks\n */\n const batchDeleteDataSinks = useCallback(\n async (ids: string[]): Promise<{ id: string; deletedAt: string }[]> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<BatchDeleteDataSinksResult>({\n mutation: BatchDeleteDataSinksDocument,\n variables: { ids },\n });\n\n if (result.data?.batchDeleteDataSinks) {\n ids.forEach(removeDataSink);\n return result.data.batchDeleteDataSinks;\n }\n\n return [];\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to batch delete DataSinks');\n setError(error);\n return [];\n } finally {\n setLoading(false);\n }\n },\n [client, removeDataSink]\n );\n\n /**\n * Refetch all datasinks\n */\n const refetch = useCallback(async () => {\n await fetchDataSinks();\n }, [fetchDataSinks]);\n\n // Auto-fetch on mount if not skipped\n useEffect(() => {\n if (!skipFetch) {\n fetchDataSinks();\n }\n }, [skipFetch, fetchDataSinks]);\n\n return {\n loading,\n error,\n dataSinks,\n fetchDataSinks,\n fetchDataSink,\n getDataSinkData,\n createDataSink,\n updateDataSink,\n deleteDataSink,\n refreshDataSink,\n invalidateCache,\n batchCreateDataSinks,\n batchDeleteDataSinks,\n refetch,\n };\n}\n\nexport default useDataSinkOperations;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useDataSinkOperations.js","names":[],"sources":["../../../src/bigconsole/hooks/useDataSinkOperations.ts"],"sourcesContent":["/**\n * DataSink Operations Hook\n *\n * Custom hook for DataSink CRUD operations with Apollo Client.\n * Integrates with the DataSink Zustand store for state management.\n */\n\nimport { useState, useMemo, useCallback, useEffect } from 'react';\nimport { useApolloClient } from '@apollo/client/react';\nimport { useDataSinkStore } from '../store/dataSinkStore';\nimport {\n ListDataSinksDocument,\n GetDataSinkDocument,\n GetDataSinkDataDocument,\n CreateDataSinkDocument,\n UpdateDataSinkDocument,\n DeleteDataSinkDocument,\n RefreshDataSinkDocument,\n InvalidateDataSinkCacheDocument,\n BatchCreateDataSinksDocument,\n BatchDeleteDataSinksDocument,\n} from '../../generated/wspace-operations';\nimport type { DataSink, DataSinkStatus, CreateDataSinkInput, UpdateDataSinkInput } from '../types';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface UseDataSinkOperationsOptions {\n workspaceId?: string;\n skipFetch?: boolean;\n}\n\ninterface DataSinkFilters {\n status?: DataSinkStatus;\n isActive?: boolean;\n search?: string;\n}\n\ninterface RefreshDataSinkInput {\n id: string;\n forceRefresh?: boolean;\n}\n\ninterface InvalidateCacheResult {\n count: number;\n invalidatedIds: string[];\n success: boolean;\n message?: string;\n}\n\ninterface DataSinkDataResult {\n raw: unknown;\n extracted: unknown;\n cached: boolean;\n version: number;\n fetchedAt: string;\n // v3.0: pagination metadata (non-null totalCount only for array payloads).\n totalCount?: number | null;\n limit?: number | null;\n offset?: number | null;\n hasMore?: boolean | null;\n}\n\n// GraphQL Query/Mutation Result Types\ninterface ListDataSinksResult {\n listDataSinks?: {\n edges: Array<{ node: unknown }>;\n pageInfo?: { hasNextPage: boolean; endCursor?: string };\n };\n}\n\ninterface GetDataSinkResult {\n getDataSink?: unknown;\n}\n\ninterface GetDataSinkDataResult {\n getDataSinkData?: DataSinkDataResult;\n}\n\ninterface CreateDataSinkResult {\n createDataSink?: unknown;\n}\n\ninterface UpdateDataSinkResult {\n updateDataSink?: unknown;\n}\n\ninterface DeleteDataSinkResult {\n deleteDataSink?: boolean;\n}\n\ninterface RefreshDataSinkResult {\n refreshDataSink?: unknown;\n}\n\ninterface InvalidateDataSinkCacheResult {\n invalidateDataSinkCache?: InvalidateCacheResult;\n}\n\ninterface BatchCreateDataSinksResult {\n batchCreateDataSinks?: unknown[];\n}\n\ninterface BatchDeleteDataSinksResult {\n batchDeleteDataSinks?: Array<{ id: string; deletedAt: string }>;\n}\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Normalize GraphQL response to DataSink type\n */\nfunction normalizeDataSink(data: unknown): DataSink {\n const raw = data as Record<string, unknown>;\n return {\n id: raw.id as string,\n workspaceId: raw.workspaceId as string,\n tenantId: raw.tenantId as string,\n name: (raw.name as string) || null,\n description: (raw.description as string) || null,\n key: raw.key as string,\n fluidGridsWorkflowId: (raw.fluidGridsWorkflowId as string) || null,\n fluidGridsWorkflowExecutionId: (raw.fluidGridsWorkflowExecutionId as string) || null,\n config: (raw.config as Record<string, unknown>) || null,\n data: (raw.data as Record<string, unknown>) || {},\n dataStats: (raw.dataStats as DataSink['dataStats']) || null,\n lastRefreshedAt: (raw.lastRefreshedAt as string) || new Date().toISOString(),\n lastRefreshedBy: (raw.lastRefreshedBy as string) || null,\n expiresAt: (raw.expiresAt as string) || null,\n nextRefreshAt: (raw.nextRefreshAt as string) || null,\n ttl: (raw.ttl as number) ?? 0,\n version: (raw.version as number) ?? 1,\n sizeBytes: (raw.sizeBytes as number) ?? 0,\n isActive: (raw.isActive as boolean) ?? true,\n fieldMaskingEnabled: (raw.fieldMaskingEnabled as boolean) ?? false,\n fieldMaskingRules: (raw.fieldMaskingRules as Record<string, unknown>) || null,\n status: (raw.status as DataSink['status']) || 'ready',\n statusChangedAt: (raw.statusChangedAt as string) || null,\n statusChangedBy: (raw.statusChangedBy as string) || null,\n setupState: (raw.setupState as DataSink['setupState']) || 'not_started',\n refresh: (raw.refresh as DataSink['refresh']) || null,\n exports: (raw.exports as DataSink['exports']) || null,\n alerts: (raw.alerts as DataSink['alerts']) || null,\n quota: (raw.quota as DataSink['quota']) || null,\n workflowLinks: raw.workflowLinks as DataSink['workflowLinks'],\n linkedWorkflowCount: raw.linkedWorkflowCount as number | undefined,\n metadata: (raw.metadata as Record<string, unknown>) || null,\n createdAt: raw.createdAt as string,\n createdBy: (raw.createdBy as string) || '',\n updatedAt: raw.updatedAt as string,\n updatedBy: (raw.updatedBy as string) || '',\n deletedAt: (raw.deletedAt as string) || null,\n deletedBy: (raw.deletedBy as string) || null,\n };\n}\n\n// ============================================================================\n// HOOK\n// ============================================================================\n\nexport function useDataSinkOperations(options: UseDataSinkOperationsOptions = {}) {\n const { workspaceId, skipFetch = false } = options;\n\n // Local state\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Apollo client\n const client = useApolloClient();\n\n // Store selectors and actions\n const dataSinksMap = useDataSinkStore((state) => state.dataSinks);\n const setDataSinks = useDataSinkStore((state) => state.setDataSinks);\n const addDataSink = useDataSinkStore((state) => state.addDataSink);\n const updateDataSinkInStore = useDataSinkStore((state) => state.updateDataSink);\n const removeDataSink = useDataSinkStore((state) => state.removeDataSink);\n const setDataSinkRefreshing = useDataSinkStore((state) => state.setDataSinkRefreshing);\n\n // Memoized array of datasinks\n const dataSinks = useMemo(() => Array.from(dataSinksMap.values()), [dataSinksMap]);\n\n // ============================================================================\n // OPERATIONS\n // ============================================================================\n\n /**\n * Fetch all datasinks with optional filters\n */\n const fetchDataSinks = useCallback(\n async (filters?: DataSinkFilters): Promise<DataSink[]> => {\n try {\n setLoading(true);\n setError(null);\n // Fail closed while the authoritative workspace list is refreshed.\n // A persisted Zustand snapshot may contain sinks deleted by another\n // tab, an API client, or an E2E harness; proposal pickers must never\n // bind to those stale records while this network request is pending.\n setDataSinks([]);\n\n const result = await client.query<ListDataSinksResult>({\n query: ListDataSinksDocument,\n variables: {\n status: filters?.status,\n isActive: filters?.isActive,\n search: filters?.search,\n },\n fetchPolicy: 'network-only',\n });\n\n const edges = result.data?.listDataSinks?.edges || [];\n const normalized = edges.map((edge: { node: unknown }) => normalizeDataSink(edge.node));\n\n setDataSinks(normalized);\n return normalized;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to fetch DataSinks');\n setError(error);\n // Do not retain a previously permitted resource set after an auth,\n // workspace-context, or network failure.\n setDataSinks([]);\n return [];\n } finally {\n setLoading(false);\n }\n },\n [client, setDataSinks]\n );\n\n /**\n * Fetch a single datasink by ID\n */\n const fetchDataSink = useCallback(\n async (id: string): Promise<DataSink | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.query<GetDataSinkResult>({\n query: GetDataSinkDocument,\n variables: { id },\n fetchPolicy: 'network-only',\n });\n\n if (result.data?.getDataSink) {\n const normalized = normalizeDataSink(result.data.getDataSink);\n addDataSink(normalized);\n return normalized;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to fetch DataSink');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client, addDataSink]\n );\n\n /**\n * Get datasink data for preview\n */\n const getDataSinkData = useCallback(\n async (\n idOrKey: { id?: string; key?: string },\n options?: {\n dataPath?: string;\n forceRefresh?: boolean;\n // v3.0: server-side pagination + filter for array payloads.\n limit?: number;\n offset?: number;\n filter?: Array<{ field: string; op: string; value: unknown }>;\n }\n ): Promise<DataSinkDataResult | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.query<GetDataSinkDataResult>({\n query: GetDataSinkDataDocument,\n variables: {\n id: idOrKey.id,\n key: idOrKey.key,\n dataPath: options?.dataPath,\n forceRefresh: options?.forceRefresh,\n limit: options?.limit,\n offset: options?.offset,\n filter: options?.filter,\n },\n fetchPolicy: 'network-only',\n });\n\n return result.data?.getDataSinkData || null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to get DataSink data');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client]\n );\n\n /**\n * Create a new datasink\n */\n const createDataSink = useCallback(\n async (input: CreateDataSinkInput): Promise<DataSink | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<CreateDataSinkResult>({\n mutation: CreateDataSinkDocument,\n variables: { input },\n });\n\n if (result.data?.createDataSink) {\n const normalized = normalizeDataSink(result.data.createDataSink);\n addDataSink(normalized);\n return normalized;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to create DataSink');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client, addDataSink]\n );\n\n /**\n * Update an existing datasink\n */\n const updateDataSink = useCallback(\n async (input: UpdateDataSinkInput): Promise<DataSink | null> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<UpdateDataSinkResult>({\n mutation: UpdateDataSinkDocument,\n variables: { input },\n });\n\n if (result.data?.updateDataSink) {\n const normalized = normalizeDataSink(result.data.updateDataSink);\n updateDataSinkInStore(normalized.id, normalized);\n return normalized;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to update DataSink');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client, updateDataSinkInStore]\n );\n\n /**\n * Delete a datasink\n */\n const deleteDataSink = useCallback(\n async (id: string): Promise<boolean> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<DeleteDataSinkResult>({\n mutation: DeleteDataSinkDocument,\n variables: { id },\n });\n\n if (result.data?.deleteDataSink) {\n removeDataSink(id);\n return true;\n }\n\n return false;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to delete DataSink');\n setError(error);\n return false;\n } finally {\n setLoading(false);\n }\n },\n [client, removeDataSink]\n );\n\n /**\n * Refresh a datasink's data\n */\n const refreshDataSink = useCallback(\n async (input: RefreshDataSinkInput): Promise<DataSink | null> => {\n try {\n setLoading(true);\n setError(null);\n\n if (input.id) {\n setDataSinkRefreshing(input.id, true);\n }\n\n const result = await client.mutate<RefreshDataSinkResult>({\n mutation: RefreshDataSinkDocument,\n variables: { input },\n });\n\n if (result.data?.refreshDataSink) {\n const normalized = normalizeDataSink(result.data.refreshDataSink);\n updateDataSinkInStore(normalized.id, normalized);\n setDataSinkRefreshing(normalized.id, false);\n return normalized;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to refresh DataSink');\n setError(error);\n if (input.id) {\n setDataSinkRefreshing(input.id, false);\n }\n return null;\n } finally {\n setLoading(false);\n }\n },\n [client, updateDataSinkInStore, setDataSinkRefreshing]\n );\n\n /**\n * Invalidate datasink cache\n */\n const invalidateCache = useCallback(\n async (id?: string): Promise<InvalidateCacheResult> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<InvalidateDataSinkCacheResult>({\n mutation: InvalidateDataSinkCacheDocument,\n variables: {\n id,\n },\n });\n\n return (\n result.data?.invalidateDataSinkCache || {\n count: 0,\n invalidatedIds: [],\n success: false,\n }\n );\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to invalidate cache');\n setError(error);\n return {\n count: 0,\n invalidatedIds: [],\n success: false,\n message: error.message,\n };\n } finally {\n setLoading(false);\n }\n },\n [client, workspaceId]\n );\n\n /**\n * Batch create datasinks\n */\n const batchCreateDataSinks = useCallback(\n async (inputs: CreateDataSinkInput[]): Promise<DataSink[]> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<BatchCreateDataSinksResult>({\n mutation: BatchCreateDataSinksDocument,\n variables: { inputs },\n });\n\n if (result.data?.batchCreateDataSinks) {\n const normalized = result.data.batchCreateDataSinks.map(normalizeDataSink);\n normalized.forEach(addDataSink);\n return normalized;\n }\n\n return [];\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to batch create DataSinks');\n setError(error);\n return [];\n } finally {\n setLoading(false);\n }\n },\n [client, addDataSink]\n );\n\n /**\n * Batch delete datasinks\n */\n const batchDeleteDataSinks = useCallback(\n async (ids: string[]): Promise<{ id: string; deletedAt: string }[]> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await client.mutate<BatchDeleteDataSinksResult>({\n mutation: BatchDeleteDataSinksDocument,\n variables: { ids },\n });\n\n if (result.data?.batchDeleteDataSinks) {\n ids.forEach(removeDataSink);\n return result.data.batchDeleteDataSinks;\n }\n\n return [];\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to batch delete DataSinks');\n setError(error);\n return [];\n } finally {\n setLoading(false);\n }\n },\n [client, removeDataSink]\n );\n\n /**\n * Refetch all datasinks\n */\n const refetch = useCallback(async () => {\n await fetchDataSinks();\n }, [fetchDataSinks]);\n\n // Auto-fetch on mount if not skipped\n useEffect(() => {\n if (!skipFetch) {\n fetchDataSinks();\n }\n }, [skipFetch, fetchDataSinks]);\n\n return {\n loading,\n error,\n dataSinks,\n fetchDataSinks,\n fetchDataSink,\n getDataSinkData,\n createDataSink,\n updateDataSink,\n deleteDataSink,\n refreshDataSink,\n invalidateCache,\n batchCreateDataSinks,\n batchDeleteDataSinks,\n refetch,\n };\n}\n\nexport default useDataSinkOperations;\n"],"mappings":"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"}
|