@lumerahq/ui 0.5.0
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/README.md +69 -0
- package/dist/api-BNY_eNKa.js +262 -0
- package/dist/automations-DU4weMpK.js +369 -0
- package/dist/components/ApiDemo.d.ts +2 -0
- package/dist/components/ApiDemo.d.ts.map +1 -0
- package/dist/components/automation/AutomationRunList.d.ts +38 -0
- package/dist/components/automation/AutomationRunList.d.ts.map +1 -0
- package/dist/components/automation/AutomationRunner.d.ts +43 -0
- package/dist/components/automation/AutomationRunner.d.ts.map +1 -0
- package/dist/components/component-example.d.ts +2 -0
- package/dist/components/component-example.d.ts.map +1 -0
- package/dist/components/data-table/DataTable.d.ts +49 -0
- package/dist/components/data-table/DataTable.d.ts.map +1 -0
- package/dist/components/data-table/DataTableFilters.d.ts +17 -0
- package/dist/components/data-table/DataTableFilters.d.ts.map +1 -0
- package/dist/components/data-table/DataTablePagination.d.ts +12 -0
- package/dist/components/data-table/DataTablePagination.d.ts.map +1 -0
- package/dist/components/data-table/RecordSheet.d.ts +24 -0
- package/dist/components/data-table/RecordSheet.d.ts.map +1 -0
- package/dist/components/data-table/index.d.ts +5 -0
- package/dist/components/data-table/index.d.ts.map +1 -0
- package/dist/components/example.d.ts +7 -0
- package/dist/components/example.d.ts.map +1 -0
- package/dist/components/index.d.ts +23 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +61 -0
- package/dist/components/ui/alert-dialog.d.ts +19 -0
- package/dist/components/ui/alert-dialog.d.ts.map +1 -0
- package/dist/components/ui/avatar.d.ts +12 -0
- package/dist/components/ui/avatar.d.ts.map +1 -0
- package/dist/components/ui/badge.d.ts +8 -0
- package/dist/components/ui/badge.d.ts.map +1 -0
- package/dist/components/ui/button.d.ts +9 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/card.d.ts +12 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/checkbox.d.ts +4 -0
- package/dist/components/ui/checkbox.d.ts.map +1 -0
- package/dist/components/ui/combobox.d.ts +25 -0
- package/dist/components/ui/combobox.d.ts.map +1 -0
- package/dist/components/ui/dialog.d.ts +18 -0
- package/dist/components/ui/dialog.d.ts.map +1 -0
- package/dist/components/ui/dropdown-menu.d.ts +26 -0
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
- package/dist/components/ui/field.d.ts +25 -0
- package/dist/components/ui/field.d.ts.map +1 -0
- package/dist/components/ui/file-dropzone.d.ts +23 -0
- package/dist/components/ui/file-dropzone.d.ts.map +1 -0
- package/dist/components/ui/input-group.d.ts +19 -0
- package/dist/components/ui/input-group.d.ts.map +1 -0
- package/dist/components/ui/input.d.ts +4 -0
- package/dist/components/ui/input.d.ts.map +1 -0
- package/dist/components/ui/label.d.ts +4 -0
- package/dist/components/ui/label.d.ts.map +1 -0
- package/dist/components/ui/popover.d.ts +10 -0
- package/dist/components/ui/popover.d.ts.map +1 -0
- package/dist/components/ui/progress.d.ts +8 -0
- package/dist/components/ui/progress.d.ts.map +1 -0
- package/dist/components/ui/scroll-area.d.ts +5 -0
- package/dist/components/ui/scroll-area.d.ts.map +1 -0
- package/dist/components/ui/select.d.ts +16 -0
- package/dist/components/ui/select.d.ts.map +1 -0
- package/dist/components/ui/separator.d.ts +4 -0
- package/dist/components/ui/separator.d.ts.map +1 -0
- package/dist/components/ui/sheet.d.ts +15 -0
- package/dist/components/ui/sheet.d.ts.map +1 -0
- package/dist/components/ui/sidebar.d.ts +64 -0
- package/dist/components/ui/sidebar.d.ts.map +1 -0
- package/dist/components/ui/skeleton.d.ts +3 -0
- package/dist/components/ui/skeleton.d.ts.map +1 -0
- package/dist/components/ui/status-badge.d.ts +21 -0
- package/dist/components/ui/status-badge.d.ts.map +1 -0
- package/dist/components/ui/table.d.ts +11 -0
- package/dist/components/ui/table.d.ts.map +1 -0
- package/dist/components/ui/tabs.d.ts +11 -0
- package/dist/components/ui/tabs.d.ts.map +1 -0
- package/dist/components/ui/textarea.d.ts +4 -0
- package/dist/components/ui/textarea.d.ts.map +1 -0
- package/dist/components/ui/tooltip.d.ts +7 -0
- package/dist/components/ui/tooltip.d.ts.map +1 -0
- package/dist/formatters-Baj7FkeG.js +3217 -0
- package/dist/hooks/index.d.ts +6 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +11 -0
- package/dist/hooks/use-automation-agent.d.ts +60 -0
- package/dist/hooks/use-automation-agent.d.ts.map +1 -0
- package/dist/hooks/use-automation-run.d.ts +65 -0
- package/dist/hooks/use-automation-run.d.ts.map +1 -0
- package/dist/hooks/use-file-upload.d.ts +57 -0
- package/dist/hooks/use-file-upload.d.ts.map +1 -0
- package/dist/hooks/use-lumera-api.d.ts +14 -0
- package/dist/hooks/use-lumera-api.d.ts.map +1 -0
- package/dist/hooks/use-mobile.d.ts +2 -0
- package/dist/hooks/use-mobile.d.ts.map +1 -0
- package/dist/hooks/use-sql-table.d.ts +52 -0
- package/dist/hooks/use-sql-table.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +134 -0
- package/dist/lib/api.d.ts +307 -0
- package/dist/lib/api.d.ts.map +1 -0
- package/dist/lib/automations.d.ts +274 -0
- package/dist/lib/automations.d.ts.map +1 -0
- package/dist/lib/bridge.d.ts +137 -0
- package/dist/lib/bridge.d.ts.map +1 -0
- package/dist/lib/formatters.d.ts +92 -0
- package/dist/lib/formatters.d.ts.map +1 -0
- package/dist/lib/index.d.ts +12 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +66 -0
- package/dist/lib/query-client.d.ts +3 -0
- package/dist/lib/query-client.d.ts.map +1 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/logo.png +0 -0
- package/dist/lumera-logo.png +0 -0
- package/dist/query-client-DdOWay4_.js +17 -0
- package/dist/tooltip-BMqvkb5u.js +17305 -0
- package/dist/ui.css +4911 -0
- package/dist/use-automation-run-CbhXCKvM.js +134 -0
- package/dist/use-sql-table-DuIu8eMY.js +343 -0
- package/package.json +79 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { useQueryClient, useMutation, useQuery } from "@tanstack/react-query";
|
|
2
|
+
import { useState, useRef, useEffect, useCallback } from "react";
|
|
3
|
+
import { h as ensureAutomationRun, f as createAutomationRun, c as cancelAutomationRun, b as automationStatuses, l as getAutomationRun } from "./automations-DU4weMpK.js";
|
|
4
|
+
function useAutomationRun(options) {
|
|
5
|
+
const {
|
|
6
|
+
agentId,
|
|
7
|
+
inputs,
|
|
8
|
+
externalId,
|
|
9
|
+
autoStart = true,
|
|
10
|
+
pollIntervalMs = 2e3,
|
|
11
|
+
dedupeWithExternalId = true,
|
|
12
|
+
onComplete,
|
|
13
|
+
onCreated,
|
|
14
|
+
onUpdate
|
|
15
|
+
} = options;
|
|
16
|
+
const queryClient = useQueryClient();
|
|
17
|
+
const [currentRunId, setCurrentRunId] = useState(null);
|
|
18
|
+
const [errorV, setErrorV] = useState(null);
|
|
19
|
+
const startMutation = useMutation({
|
|
20
|
+
mutationFn: async () => {
|
|
21
|
+
setErrorV(null);
|
|
22
|
+
if (!agentId) throw new Error("agentId is required");
|
|
23
|
+
if (externalId && dedupeWithExternalId) {
|
|
24
|
+
return ensureAutomationRun({ agentId, inputs, externalId });
|
|
25
|
+
}
|
|
26
|
+
return createAutomationRun({ agentId, inputs, externalId });
|
|
27
|
+
},
|
|
28
|
+
onSuccess: (data) => {
|
|
29
|
+
setCurrentRunId(data.id);
|
|
30
|
+
queryClient.setQueryData(["automation-run", data.id], data);
|
|
31
|
+
onCreated?.(data);
|
|
32
|
+
onUpdate?.(data);
|
|
33
|
+
},
|
|
34
|
+
onError: (err) => {
|
|
35
|
+
setErrorV(err instanceof Error ? err.message : "Unable to start automation");
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
const cancelMutation = useMutation({
|
|
39
|
+
mutationFn: async (runId) => {
|
|
40
|
+
return cancelAutomationRun(runId);
|
|
41
|
+
},
|
|
42
|
+
onSuccess: (data) => {
|
|
43
|
+
queryClient.setQueryData(["automation-run", data.id], data);
|
|
44
|
+
onUpdate?.(data);
|
|
45
|
+
},
|
|
46
|
+
onError: (err) => {
|
|
47
|
+
setErrorV(err instanceof Error ? err.message : "Unable to cancel automation");
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
const {
|
|
51
|
+
data: run,
|
|
52
|
+
isError: isQueryError,
|
|
53
|
+
error: queryError,
|
|
54
|
+
refetch
|
|
55
|
+
} = useQuery({
|
|
56
|
+
queryKey: ["automation-run", currentRunId],
|
|
57
|
+
queryFn: async () => {
|
|
58
|
+
if (!currentRunId) return null;
|
|
59
|
+
return getAutomationRun(currentRunId);
|
|
60
|
+
},
|
|
61
|
+
enabled: !!currentRunId,
|
|
62
|
+
// Poll while active, stop when terminal
|
|
63
|
+
refetchInterval: (query) => {
|
|
64
|
+
const status = query.state.data?.status;
|
|
65
|
+
if (status && automationStatuses.TERMINAL_STATUSES.includes(status)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return pollIntervalMs;
|
|
69
|
+
},
|
|
70
|
+
// Immediately show data from setQueryData
|
|
71
|
+
initialData: void 0
|
|
72
|
+
});
|
|
73
|
+
const completedRunRef = useRef(null);
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (run) {
|
|
76
|
+
onUpdate?.(run);
|
|
77
|
+
if (automationStatuses.TERMINAL_STATUSES.includes(run.status)) {
|
|
78
|
+
if (completedRunRef.current !== run.id) {
|
|
79
|
+
completedRunRef.current = run.id;
|
|
80
|
+
onComplete?.(run);
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
if (completedRunRef.current === run.id) {
|
|
84
|
+
completedRunRef.current = null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}, [run, onComplete, onUpdate]);
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
if (isQueryError && queryError) {
|
|
91
|
+
setErrorV(queryError instanceof Error ? queryError.message : "Failed to poll automation");
|
|
92
|
+
}
|
|
93
|
+
}, [isQueryError, queryError]);
|
|
94
|
+
const start = useCallback(async () => {
|
|
95
|
+
try {
|
|
96
|
+
const result = await startMutation.mutateAsync();
|
|
97
|
+
return result;
|
|
98
|
+
} catch {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}, [startMutation]);
|
|
102
|
+
const cancel = useCallback(async () => {
|
|
103
|
+
if (!currentRunId) return null;
|
|
104
|
+
try {
|
|
105
|
+
return await cancelMutation.mutateAsync(currentRunId);
|
|
106
|
+
} catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}, [cancelMutation, currentRunId]);
|
|
110
|
+
const refresh = useCallback(async () => {
|
|
111
|
+
if (!currentRunId) return start();
|
|
112
|
+
const result = await refetch();
|
|
113
|
+
return result.data ?? null;
|
|
114
|
+
}, [currentRunId, refetch, start]);
|
|
115
|
+
const autoStartedRef = useRef(false);
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (autoStart && !autoStartedRef.current && !currentRunId) {
|
|
118
|
+
autoStartedRef.current = true;
|
|
119
|
+
void start();
|
|
120
|
+
}
|
|
121
|
+
}, [autoStart, currentRunId, start]);
|
|
122
|
+
return {
|
|
123
|
+
run: run ?? null,
|
|
124
|
+
status: run?.status ?? "idle",
|
|
125
|
+
isLoading: startMutation.isPending || cancelMutation.isPending,
|
|
126
|
+
error: errorV,
|
|
127
|
+
start,
|
|
128
|
+
refresh,
|
|
129
|
+
cancel
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
export {
|
|
133
|
+
useAutomationRun as u
|
|
134
|
+
};
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { useQueryClient, useQuery, useMutation } from "@tanstack/react-query";
|
|
2
|
+
import { useState, useCallback, useEffect, useMemo } from "react";
|
|
3
|
+
import { w as listRunsByAgent, b as automationStatuses, f as createAutomationRun, o as onInitMessage } from "./automations-DU4weMpK.js";
|
|
4
|
+
import { u as uploadFile, i as pbSql, k as pbUpdateRecord } from "./api-BNY_eNKa.js";
|
|
5
|
+
function useAutomationAgent({
|
|
6
|
+
agentId,
|
|
7
|
+
limit = 5,
|
|
8
|
+
pollInterval = 3e3,
|
|
9
|
+
enabled = true
|
|
10
|
+
}) {
|
|
11
|
+
const queryClient = useQueryClient();
|
|
12
|
+
const [page, setPage] = useState(0);
|
|
13
|
+
const [isStarting, setIsStarting] = useState(false);
|
|
14
|
+
const [startError, setStartError] = useState(null);
|
|
15
|
+
const queryKey = ["automation-runs", agentId, page];
|
|
16
|
+
const {
|
|
17
|
+
data: runs = [],
|
|
18
|
+
isLoading,
|
|
19
|
+
error: fetchError
|
|
20
|
+
} = useQuery({
|
|
21
|
+
queryKey,
|
|
22
|
+
queryFn: () => listRunsByAgent({
|
|
23
|
+
agentId,
|
|
24
|
+
limit,
|
|
25
|
+
offset: page * limit
|
|
26
|
+
}),
|
|
27
|
+
enabled: enabled && !!agentId,
|
|
28
|
+
refetchInterval: (query) => {
|
|
29
|
+
const hasActive = query.state.data?.some((run) => automationStatuses.ACTIVE_STATUSES.includes(run.status));
|
|
30
|
+
return hasActive ? pollInterval : false;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
const startRun = useCallback(
|
|
34
|
+
async (inputs, options) => {
|
|
35
|
+
setIsStarting(true);
|
|
36
|
+
setStartError(null);
|
|
37
|
+
try {
|
|
38
|
+
const run = await createAutomationRun({
|
|
39
|
+
agentId,
|
|
40
|
+
inputs,
|
|
41
|
+
metadata: options?.metadata
|
|
42
|
+
});
|
|
43
|
+
setPage(0);
|
|
44
|
+
await queryClient.invalidateQueries({ queryKey: ["automation-runs", agentId] });
|
|
45
|
+
options?.onSuccess?.(run);
|
|
46
|
+
return run;
|
|
47
|
+
} catch (err) {
|
|
48
|
+
const message = err instanceof Error ? err.message : "Failed to start automation";
|
|
49
|
+
setStartError(message);
|
|
50
|
+
console.error("Automation start failed:", err);
|
|
51
|
+
options?.onError?.(err);
|
|
52
|
+
throw err;
|
|
53
|
+
} finally {
|
|
54
|
+
setIsStarting(false);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
[agentId, queryClient]
|
|
58
|
+
);
|
|
59
|
+
const refresh = useCallback(() => {
|
|
60
|
+
return queryClient.invalidateQueries({ queryKey: ["automation-runs", agentId] });
|
|
61
|
+
}, [agentId, queryClient]);
|
|
62
|
+
return {
|
|
63
|
+
/** The list of automation runs for the current page. */
|
|
64
|
+
runs,
|
|
65
|
+
/** Whether the initial runs are loading. */
|
|
66
|
+
isLoading,
|
|
67
|
+
/** Error from fetching runs. */
|
|
68
|
+
fetchError,
|
|
69
|
+
/** Whether a startRun request is in progress. */
|
|
70
|
+
isStarting,
|
|
71
|
+
/** Error from the last startRun attempt. */
|
|
72
|
+
startError,
|
|
73
|
+
/** Current page index (0-based). */
|
|
74
|
+
page,
|
|
75
|
+
/** Set the current page index. */
|
|
76
|
+
setPage,
|
|
77
|
+
/** Function to start a new run. */
|
|
78
|
+
startRun,
|
|
79
|
+
/** Manually refresh the list. */
|
|
80
|
+
refresh,
|
|
81
|
+
/** Whether there are more items (heuristic based on limit). */
|
|
82
|
+
hasMore: runs.length === limit
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function useFileUpload(options) {
|
|
86
|
+
const { collectionId, fieldName, onSuccess, onError } = options;
|
|
87
|
+
const [status, setStatus] = useState("idle");
|
|
88
|
+
const [error, setError] = useState(null);
|
|
89
|
+
const [result, setResult] = useState(null);
|
|
90
|
+
const upload = useCallback(
|
|
91
|
+
async (file) => {
|
|
92
|
+
setStatus("uploading");
|
|
93
|
+
setError(null);
|
|
94
|
+
setResult(null);
|
|
95
|
+
try {
|
|
96
|
+
const uploadResult = await uploadFile(file, { collectionId, fieldName });
|
|
97
|
+
setStatus("success");
|
|
98
|
+
setResult(uploadResult);
|
|
99
|
+
onSuccess?.(uploadResult);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
const message = err instanceof Error ? err.message : "Upload failed";
|
|
102
|
+
setStatus("error");
|
|
103
|
+
setError(message);
|
|
104
|
+
onError?.(err instanceof Error ? err : new Error(message));
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
[collectionId, fieldName, onSuccess, onError]
|
|
108
|
+
);
|
|
109
|
+
const reset = useCallback(() => {
|
|
110
|
+
setStatus("idle");
|
|
111
|
+
setError(null);
|
|
112
|
+
setResult(null);
|
|
113
|
+
}, []);
|
|
114
|
+
return {
|
|
115
|
+
status,
|
|
116
|
+
error,
|
|
117
|
+
result,
|
|
118
|
+
upload,
|
|
119
|
+
reset,
|
|
120
|
+
isUploading: status === "uploading"
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function usePbSql(request, options) {
|
|
124
|
+
return useQuery({
|
|
125
|
+
queryKey: ["pbSql", request],
|
|
126
|
+
queryFn: () => pbSql(request),
|
|
127
|
+
...options
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
function usePbUpdate(options) {
|
|
131
|
+
const queryClient = useQueryClient();
|
|
132
|
+
return useMutation({
|
|
133
|
+
mutationFn: ({ collectionId, recordId, data }) => pbUpdateRecord(collectionId, recordId, data),
|
|
134
|
+
onSuccess: (...args) => {
|
|
135
|
+
queryClient.invalidateQueries({ queryKey: ["pbSql"] });
|
|
136
|
+
options?.onSuccess?.(...args);
|
|
137
|
+
},
|
|
138
|
+
...options
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
function useLumeraUser() {
|
|
142
|
+
const [hostData, setHostData] = useState(null);
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
const cleanup = onInitMessage((payload) => {
|
|
145
|
+
if (payload) {
|
|
146
|
+
setHostData(payload);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
return () => {
|
|
150
|
+
cleanup();
|
|
151
|
+
};
|
|
152
|
+
}, []);
|
|
153
|
+
return hostData;
|
|
154
|
+
}
|
|
155
|
+
function buildFilterClause(filters) {
|
|
156
|
+
if (filters.length === 0) {
|
|
157
|
+
return { clause: "", args: [] };
|
|
158
|
+
}
|
|
159
|
+
const conditions = [];
|
|
160
|
+
const args = [];
|
|
161
|
+
filters.forEach((filter) => {
|
|
162
|
+
const col = filter.column;
|
|
163
|
+
switch (filter.operator) {
|
|
164
|
+
case "eq":
|
|
165
|
+
conditions.push(`${col} = ?`);
|
|
166
|
+
args.push(filter.value);
|
|
167
|
+
break;
|
|
168
|
+
case "neq":
|
|
169
|
+
conditions.push(`${col} != ?`);
|
|
170
|
+
args.push(filter.value);
|
|
171
|
+
break;
|
|
172
|
+
case "gt":
|
|
173
|
+
conditions.push(`${col} > ?`);
|
|
174
|
+
args.push(filter.value);
|
|
175
|
+
break;
|
|
176
|
+
case "gte":
|
|
177
|
+
conditions.push(`${col} >= ?`);
|
|
178
|
+
args.push(filter.value);
|
|
179
|
+
break;
|
|
180
|
+
case "lt":
|
|
181
|
+
conditions.push(`${col} < ?`);
|
|
182
|
+
args.push(filter.value);
|
|
183
|
+
break;
|
|
184
|
+
case "lte":
|
|
185
|
+
conditions.push(`${col} <= ?`);
|
|
186
|
+
args.push(filter.value);
|
|
187
|
+
break;
|
|
188
|
+
case "like":
|
|
189
|
+
conditions.push(`${col} LIKE ?`);
|
|
190
|
+
args.push(`%${filter.value}%`);
|
|
191
|
+
break;
|
|
192
|
+
case "in":
|
|
193
|
+
if (Array.isArray(filter.value) && filter.value.length > 0) {
|
|
194
|
+
const placeholders = filter.value.map(() => "?").join(", ");
|
|
195
|
+
conditions.push(`${col} IN (${placeholders})`);
|
|
196
|
+
args.push(...filter.value);
|
|
197
|
+
}
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
return {
|
|
202
|
+
clause: conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "",
|
|
203
|
+
args
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function buildOrderClause(sort) {
|
|
207
|
+
if (!sort) return "";
|
|
208
|
+
return `ORDER BY ${sort.column} ${sort.direction.toUpperCase()}`;
|
|
209
|
+
}
|
|
210
|
+
function useSqlTable(options) {
|
|
211
|
+
const {
|
|
212
|
+
baseSql,
|
|
213
|
+
queryKey = "sqlTable",
|
|
214
|
+
initialPage = 1,
|
|
215
|
+
initialPageSize = 25,
|
|
216
|
+
initialSort = null,
|
|
217
|
+
initialFilters = [],
|
|
218
|
+
enabled = true,
|
|
219
|
+
transformRow
|
|
220
|
+
} = options;
|
|
221
|
+
const queryClient = useQueryClient();
|
|
222
|
+
const [page, setPage] = useState(initialPage);
|
|
223
|
+
const [pageSize, setPageSize] = useState(initialPageSize);
|
|
224
|
+
const [sort, setSort] = useState(initialSort);
|
|
225
|
+
const [filters, setFilters] = useState(initialFilters);
|
|
226
|
+
const { sql, args } = useMemo(() => {
|
|
227
|
+
const { clause: whereClause, args: whereArgs } = buildFilterClause(filters);
|
|
228
|
+
const orderClause = buildOrderClause(sort);
|
|
229
|
+
const offset = (page - 1) * pageSize;
|
|
230
|
+
const fullSql = `
|
|
231
|
+
SELECT * FROM (${baseSql}) AS __data
|
|
232
|
+
${whereClause}
|
|
233
|
+
${orderClause}
|
|
234
|
+
LIMIT ? OFFSET ?
|
|
235
|
+
`.trim();
|
|
236
|
+
return {
|
|
237
|
+
sql: fullSql,
|
|
238
|
+
args: [...whereArgs, pageSize, offset]
|
|
239
|
+
};
|
|
240
|
+
}, [baseSql, filters, sort, page, pageSize]);
|
|
241
|
+
const { sql: countSql, args: countArgs } = useMemo(() => {
|
|
242
|
+
const { clause: whereClause, args: whereArgs } = buildFilterClause(filters);
|
|
243
|
+
const countQuery2 = `
|
|
244
|
+
SELECT COUNT(*) as count FROM (${baseSql}) AS __data
|
|
245
|
+
${whereClause}
|
|
246
|
+
`.trim();
|
|
247
|
+
return { sql: countQuery2, args: whereArgs };
|
|
248
|
+
}, [baseSql, filters]);
|
|
249
|
+
const dataQuery = useQuery({
|
|
250
|
+
queryKey: [queryKey, "data", sql, args],
|
|
251
|
+
queryFn: () => pbSql({ sql, args }),
|
|
252
|
+
enabled
|
|
253
|
+
});
|
|
254
|
+
const countQuery = useQuery({
|
|
255
|
+
queryKey: [queryKey, "count", countSql, countArgs],
|
|
256
|
+
queryFn: () => pbSql({ sql: countSql, args: countArgs }),
|
|
257
|
+
enabled
|
|
258
|
+
});
|
|
259
|
+
const rows = useMemo(() => {
|
|
260
|
+
const rawRows = dataQuery.data?.rows ?? [];
|
|
261
|
+
if (transformRow) {
|
|
262
|
+
return rawRows.map(transformRow);
|
|
263
|
+
}
|
|
264
|
+
return rawRows;
|
|
265
|
+
}, [dataQuery.data?.rows, transformRow]);
|
|
266
|
+
const columns = useMemo(() => {
|
|
267
|
+
return dataQuery.data?.columns ?? [];
|
|
268
|
+
}, [dataQuery.data?.columns]);
|
|
269
|
+
const totalCount = countQuery.data?.rows?.[0]?.count ?? 0;
|
|
270
|
+
const totalPages = Math.max(1, Math.ceil(totalCount / pageSize));
|
|
271
|
+
const handleSetPage = useCallback((newPage) => {
|
|
272
|
+
setPage(Math.max(1, newPage));
|
|
273
|
+
}, []);
|
|
274
|
+
const handleSetPageSize = useCallback((newSize) => {
|
|
275
|
+
setPageSize(newSize);
|
|
276
|
+
setPage(1);
|
|
277
|
+
}, []);
|
|
278
|
+
const toggleSort = useCallback((column) => {
|
|
279
|
+
setSort((current) => {
|
|
280
|
+
if (current?.column === column) {
|
|
281
|
+
if (current.direction === "asc") {
|
|
282
|
+
return { column, direction: "desc" };
|
|
283
|
+
}
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
return { column, direction: "asc" };
|
|
287
|
+
});
|
|
288
|
+
}, []);
|
|
289
|
+
const addFilter = useCallback((filter) => {
|
|
290
|
+
setFilters((current) => {
|
|
291
|
+
const filtered = current.filter((f) => !(f.column === filter.column && f.operator === filter.operator));
|
|
292
|
+
return [...filtered, filter];
|
|
293
|
+
});
|
|
294
|
+
setPage(1);
|
|
295
|
+
}, []);
|
|
296
|
+
const removeFilter = useCallback((column, operator) => {
|
|
297
|
+
setFilters(
|
|
298
|
+
(current) => current.filter((f) => {
|
|
299
|
+
if (f.column !== column) return true;
|
|
300
|
+
if (operator && f.operator !== operator) return true;
|
|
301
|
+
return false;
|
|
302
|
+
})
|
|
303
|
+
);
|
|
304
|
+
setPage(1);
|
|
305
|
+
}, []);
|
|
306
|
+
const clearFilters = useCallback(() => {
|
|
307
|
+
setFilters([]);
|
|
308
|
+
setPage(1);
|
|
309
|
+
}, []);
|
|
310
|
+
const refresh = useCallback(() => {
|
|
311
|
+
queryClient.invalidateQueries({ queryKey: [queryKey] });
|
|
312
|
+
}, [queryClient, queryKey]);
|
|
313
|
+
return {
|
|
314
|
+
rows,
|
|
315
|
+
columns,
|
|
316
|
+
totalCount,
|
|
317
|
+
page,
|
|
318
|
+
pageSize,
|
|
319
|
+
totalPages,
|
|
320
|
+
setPage: handleSetPage,
|
|
321
|
+
setPageSize: handleSetPageSize,
|
|
322
|
+
sort,
|
|
323
|
+
setSort,
|
|
324
|
+
toggleSort,
|
|
325
|
+
filters,
|
|
326
|
+
addFilter,
|
|
327
|
+
removeFilter,
|
|
328
|
+
clearFilters,
|
|
329
|
+
setFilters,
|
|
330
|
+
isLoading: dataQuery.isLoading || countQuery.isLoading,
|
|
331
|
+
isFetching: dataQuery.isFetching || countQuery.isFetching,
|
|
332
|
+
error: dataQuery.error ?? countQuery.error ?? null,
|
|
333
|
+
refresh
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
export {
|
|
337
|
+
useFileUpload as a,
|
|
338
|
+
useLumeraUser as b,
|
|
339
|
+
usePbSql as c,
|
|
340
|
+
usePbUpdate as d,
|
|
341
|
+
useSqlTable as e,
|
|
342
|
+
useAutomationAgent as u
|
|
343
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lumerahq/ui",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"sideEffects": [
|
|
6
|
+
"*.css"
|
|
7
|
+
],
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./components": {
|
|
17
|
+
"types": "./dist/components/index.d.ts",
|
|
18
|
+
"import": "./dist/components/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./hooks": {
|
|
21
|
+
"types": "./dist/hooks/index.d.ts",
|
|
22
|
+
"import": "./dist/hooks/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./lib": {
|
|
25
|
+
"types": "./dist/lib/index.d.ts",
|
|
26
|
+
"import": "./dist/lib/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./styles.css": "./dist/ui.css"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist"
|
|
32
|
+
],
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/lumerahq/lumera.git"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
42
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@base-ui/react": "^1.0.0",
|
|
46
|
+
"@tanstack/react-query": "^5.90.12",
|
|
47
|
+
"class-variance-authority": "^0.7.1",
|
|
48
|
+
"clsx": "^2.1.1",
|
|
49
|
+
"lucide-react": "^0.561.0",
|
|
50
|
+
"tailwind-merge": "^3.4.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@biomejs/biome": "^2.3.10",
|
|
54
|
+
"@fontsource-variable/inter": "^5.2.8",
|
|
55
|
+
"@tailwindcss/vite": "^4.1.17",
|
|
56
|
+
"@types/node": "^24.10.1",
|
|
57
|
+
"@types/react": "^19.2.5",
|
|
58
|
+
"@types/react-dom": "^19.2.3",
|
|
59
|
+
"@vitejs/plugin-react": "^5.1.1",
|
|
60
|
+
"react": "^19.2.0",
|
|
61
|
+
"react-dom": "^19.2.0",
|
|
62
|
+
"shadcn": "^3.6.2",
|
|
63
|
+
"tailwindcss": "^4.1.17",
|
|
64
|
+
"tw-animate-css": "^1.4.0",
|
|
65
|
+
"typescript": "~5.9.3",
|
|
66
|
+
"vite": "^7.2.4",
|
|
67
|
+
"vite-plugin-dts": "^4.5.4"
|
|
68
|
+
},
|
|
69
|
+
"scripts": {
|
|
70
|
+
"dev": "vite",
|
|
71
|
+
"build": "tsc -b && vite build",
|
|
72
|
+
"build:lib": "tsc -b && vite build --mode lib",
|
|
73
|
+
"format": "biome format --write .",
|
|
74
|
+
"lint": "biome lint --write .",
|
|
75
|
+
"check": "biome check --write .",
|
|
76
|
+
"check:ci": "biome check .",
|
|
77
|
+
"preview": "vite preview"
|
|
78
|
+
}
|
|
79
|
+
}
|