@prismiq/react 0.1.0 → 0.2.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/dist/{CustomSQLEditor-DYeId0Gp.d.ts → ChatBubble-ARocmvZD.d.cts} +48 -4
- package/dist/{CustomSQLEditor-BXB4rf1q.d.cts → ChatBubble-BN_CjIpk.d.ts} +48 -4
- package/dist/{DashboardDialog-B3vYC5Gs.d.ts → DashboardDialog-UhUGXx2h.d.ts} +6 -4
- package/dist/{DashboardDialog-LHmrtNQU.d.cts → DashboardDialog-Z-HypxmG.d.cts} +6 -4
- package/dist/{accessibility-2yy5yqRR.d.cts → accessibility-Bu2mNtaB.d.cts} +1 -1
- package/dist/{accessibility-2yy5yqRR.d.ts → accessibility-Bu2mNtaB.d.ts} +1 -1
- package/dist/charts/index.cjs +27 -27
- package/dist/charts/index.d.cts +2 -2
- package/dist/charts/index.d.ts +2 -2
- package/dist/charts/index.js +2 -2
- package/dist/{chunk-NK7HKX2J.cjs → chunk-73TPDGXB.cjs} +7 -7
- package/dist/{chunk-NK7HKX2J.cjs.map → chunk-73TPDGXB.cjs.map} +1 -1
- package/dist/{chunk-FEABEF3J.cjs → chunk-FKXCINUF.cjs} +551 -299
- package/dist/chunk-FKXCINUF.cjs.map +1 -0
- package/dist/{chunk-2H5WTH4K.js → chunk-FQ23KG6G.js} +3 -3
- package/dist/{chunk-2H5WTH4K.js.map → chunk-FQ23KG6G.js.map} +1 -1
- package/dist/{chunk-UPYINBZU.js → chunk-GELI7MDZ.js} +982 -51
- package/dist/chunk-GELI7MDZ.js.map +1 -0
- package/dist/{chunk-WWTT2OJ5.js → chunk-HKZFEXT6.js} +27 -9
- package/dist/chunk-HKZFEXT6.js.map +1 -0
- package/dist/{chunk-MOAEEF5P.js → chunk-JBJ5LEAG.js} +362 -110
- package/dist/chunk-JBJ5LEAG.js.map +1 -0
- package/dist/{chunk-4AVL6GQK.cjs → chunk-KXB2IZI2.cjs} +36 -9
- package/dist/chunk-KXB2IZI2.cjs.map +1 -0
- package/dist/{chunk-EX74SI67.js → chunk-LBE6GIBC.js} +36 -9
- package/dist/chunk-LBE6GIBC.js.map +1 -0
- package/dist/{chunk-NY6TZLST.cjs → chunk-PG7QBH3G.cjs} +988 -53
- package/dist/chunk-PG7QBH3G.cjs.map +1 -0
- package/dist/{chunk-MDXGGZSW.cjs → chunk-ZYVN6XAZ.cjs} +35 -37
- package/dist/chunk-ZYVN6XAZ.cjs.map +1 -0
- package/dist/components/index.cjs +63 -55
- package/dist/components/index.d.cts +2 -2
- package/dist/components/index.d.ts +2 -2
- package/dist/components/index.js +2 -2
- package/dist/dashboard/index.cjs +36 -36
- package/dist/dashboard/index.d.cts +7 -5
- package/dist/dashboard/index.d.ts +7 -5
- package/dist/dashboard/index.js +4 -4
- package/dist/export/index.cjs +7 -7
- package/dist/export/index.d.cts +6 -4
- package/dist/export/index.d.ts +6 -4
- package/dist/export/index.js +1 -1
- package/dist/{index-C-Qcuu4Y.d.cts → index-B8DelfpL.d.cts} +2 -2
- package/dist/{index-rPc7ijt8.d.ts → index-RbfYPQD_.d.ts} +2 -2
- package/dist/index.cjs +150 -134
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -9
- package/dist/index.d.ts +97 -9
- package/dist/index.js +7 -7
- package/dist/index.js.map +1 -1
- package/dist/{types-WrCbOeAV.d.cts → types-ccB9Ps3k.d.cts} +59 -1
- package/dist/{types-WrCbOeAV.d.ts → types-ccB9Ps3k.d.ts} +59 -1
- package/dist/utils/index.cjs +15 -15
- package/dist/utils/index.d.cts +5 -21
- package/dist/utils/index.d.ts +5 -21
- package/dist/utils/index.js +1 -1
- package/package.json +3 -7
- package/dist/chunk-4AVL6GQK.cjs.map +0 -1
- package/dist/chunk-EX74SI67.js.map +0 -1
- package/dist/chunk-FEABEF3J.cjs.map +0 -1
- package/dist/chunk-MDXGGZSW.cjs.map +0 -1
- package/dist/chunk-MOAEEF5P.js.map +0 -1
- package/dist/chunk-NY6TZLST.cjs.map +0 -1
- package/dist/chunk-UPYINBZU.js.map +0 -1
- package/dist/chunk-WWTT2OJ5.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useTheme } from './chunk-T6STUE7E.js';
|
|
2
|
-
import { useFocusTrap, parseColumnRef } from './chunk-
|
|
2
|
+
import { useFocusTrap, parseColumnRef } from './chunk-LBE6GIBC.js';
|
|
3
3
|
import { forwardRef, useState, useRef, useCallback, useEffect, isValidElement, cloneElement, createContext, useContext, useLayoutEffect, Component, useMemo } from 'react';
|
|
4
4
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
import { createPortal } from 'react-dom';
|
|
@@ -2325,6 +2325,9 @@ var PrismiqClient = class {
|
|
|
2325
2325
|
}
|
|
2326
2326
|
/**
|
|
2327
2327
|
* Make an authenticated request to the API.
|
|
2328
|
+
*
|
|
2329
|
+
* @param path - API path (starting with /)
|
|
2330
|
+
* @param options - Fetch options including signal for cancellation
|
|
2328
2331
|
*/
|
|
2329
2332
|
async request(path, options = {}) {
|
|
2330
2333
|
const url = `${this.endpoint}${path}`;
|
|
@@ -2470,6 +2473,17 @@ var PrismiqClient = class {
|
|
|
2470
2473
|
const result = await this.request(path);
|
|
2471
2474
|
return result.values;
|
|
2472
2475
|
}
|
|
2476
|
+
/**
|
|
2477
|
+
* Get data source metadata including display names and descriptions.
|
|
2478
|
+
*
|
|
2479
|
+
* Returns metadata for all exposed tables/views that can be used
|
|
2480
|
+
* to show user-friendly names in the UI instead of raw table names.
|
|
2481
|
+
*
|
|
2482
|
+
* @returns Array of data source metadata.
|
|
2483
|
+
*/
|
|
2484
|
+
async getDataSources() {
|
|
2485
|
+
return this.request("/data-sources");
|
|
2486
|
+
}
|
|
2473
2487
|
// ============================================================================
|
|
2474
2488
|
// Query Methods
|
|
2475
2489
|
// ============================================================================
|
|
@@ -2503,12 +2517,14 @@ var PrismiqClient = class {
|
|
|
2503
2517
|
*
|
|
2504
2518
|
* @param query - The query definition to execute.
|
|
2505
2519
|
* @param bypassCache - If true, bypass cache and re-execute query.
|
|
2520
|
+
* @param signal - Optional AbortSignal for cancellation.
|
|
2506
2521
|
* @returns The query result with all rows and cache metadata.
|
|
2507
2522
|
*/
|
|
2508
|
-
async executeQuery(query, bypassCache = false) {
|
|
2523
|
+
async executeQuery(query, bypassCache = false, signal) {
|
|
2509
2524
|
return this.request("/query/execute", {
|
|
2510
2525
|
method: "POST",
|
|
2511
|
-
body: JSON.stringify({ query, bypass_cache: bypassCache })
|
|
2526
|
+
body: JSON.stringify({ query, bypass_cache: bypassCache }),
|
|
2527
|
+
signal
|
|
2512
2528
|
});
|
|
2513
2529
|
}
|
|
2514
2530
|
/**
|
|
@@ -2825,6 +2841,83 @@ var PrismiqClient = class {
|
|
|
2825
2841
|
})
|
|
2826
2842
|
});
|
|
2827
2843
|
}
|
|
2844
|
+
// ============================================================================
|
|
2845
|
+
// LLM Methods
|
|
2846
|
+
// ============================================================================
|
|
2847
|
+
/**
|
|
2848
|
+
* Get the LLM agent status.
|
|
2849
|
+
*
|
|
2850
|
+
* @returns LLM status including enabled state, provider, and model.
|
|
2851
|
+
*/
|
|
2852
|
+
async getLLMStatus() {
|
|
2853
|
+
return this.request("/llm/status");
|
|
2854
|
+
}
|
|
2855
|
+
/**
|
|
2856
|
+
* Stream a chat response from the LLM agent via SSE.
|
|
2857
|
+
*
|
|
2858
|
+
* @param message - User's message.
|
|
2859
|
+
* @param history - Previous conversation messages.
|
|
2860
|
+
* @param currentSql - Current SQL in the editor (for context).
|
|
2861
|
+
* @param signal - Optional AbortSignal for cancellation.
|
|
2862
|
+
* @yields StreamChunk objects as the response is generated.
|
|
2863
|
+
*/
|
|
2864
|
+
async *streamChat(message, history, currentSql, signal) {
|
|
2865
|
+
const url = `${this.endpoint}/llm/chat`;
|
|
2866
|
+
const headers = {
|
|
2867
|
+
"Content-Type": "application/json",
|
|
2868
|
+
"X-Tenant-ID": this.tenantId
|
|
2869
|
+
};
|
|
2870
|
+
if (this.userId) headers["X-User-ID"] = this.userId;
|
|
2871
|
+
if (this.schemaName) headers["X-Schema-Name"] = this.schemaName;
|
|
2872
|
+
if (this.customHeaders) Object.assign(headers, this.customHeaders);
|
|
2873
|
+
if (this.getToken) {
|
|
2874
|
+
const token = await this.getToken();
|
|
2875
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
2876
|
+
}
|
|
2877
|
+
const response = await fetch(url, {
|
|
2878
|
+
method: "POST",
|
|
2879
|
+
headers,
|
|
2880
|
+
body: JSON.stringify({
|
|
2881
|
+
message,
|
|
2882
|
+
history,
|
|
2883
|
+
current_sql: currentSql
|
|
2884
|
+
}),
|
|
2885
|
+
signal
|
|
2886
|
+
});
|
|
2887
|
+
if (!response.ok) {
|
|
2888
|
+
throw new PrismiqError(
|
|
2889
|
+
`LLM chat failed: ${response.status} ${response.statusText}`,
|
|
2890
|
+
response.status
|
|
2891
|
+
);
|
|
2892
|
+
}
|
|
2893
|
+
const reader = response.body?.getReader();
|
|
2894
|
+
if (!reader) return;
|
|
2895
|
+
const decoder = new TextDecoder();
|
|
2896
|
+
let buffer = "";
|
|
2897
|
+
try {
|
|
2898
|
+
while (true) {
|
|
2899
|
+
const { done, value } = await reader.read();
|
|
2900
|
+
if (done) break;
|
|
2901
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2902
|
+
const lines = buffer.split("\n");
|
|
2903
|
+
buffer = lines.pop() ?? "";
|
|
2904
|
+
for (const line of lines) {
|
|
2905
|
+
if (line.startsWith("data: ")) {
|
|
2906
|
+
const data = line.slice(6).trim();
|
|
2907
|
+
if (data) {
|
|
2908
|
+
try {
|
|
2909
|
+
const chunk = JSON.parse(data);
|
|
2910
|
+
yield chunk;
|
|
2911
|
+
} catch {
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
} finally {
|
|
2918
|
+
reader.releaseLock();
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2828
2921
|
};
|
|
2829
2922
|
var AnalyticsContext = createContext(null);
|
|
2830
2923
|
var CallbacksContext = createContext({});
|
|
@@ -2861,6 +2954,7 @@ function AnalyticsProvider({
|
|
|
2861
2954
|
}
|
|
2862
2955
|
const client = clientRef.current;
|
|
2863
2956
|
const [schema, setSchema] = useState(null);
|
|
2957
|
+
const [dataSources, setDataSources] = useState([]);
|
|
2864
2958
|
const [isLoading, setIsLoading] = useState(true);
|
|
2865
2959
|
const [error, setError] = useState(null);
|
|
2866
2960
|
const hasFetchedSchemaRef = useRef(false);
|
|
@@ -2878,8 +2972,13 @@ function AnalyticsProvider({
|
|
|
2878
2972
|
setIsLoading(true);
|
|
2879
2973
|
setError(null);
|
|
2880
2974
|
try {
|
|
2881
|
-
const fetchedSchema = await
|
|
2975
|
+
const [fetchedSchema, fetchedDataSources] = await Promise.all([
|
|
2976
|
+
client.getSchema(),
|
|
2977
|
+
client.getDataSources().catch(() => [])
|
|
2978
|
+
// Non-critical, fallback to empty
|
|
2979
|
+
]);
|
|
2882
2980
|
setSchema(fetchedSchema);
|
|
2981
|
+
setDataSources(fetchedDataSources);
|
|
2883
2982
|
onSchemaLoadRef.current?.(fetchedSchema);
|
|
2884
2983
|
} catch (err) {
|
|
2885
2984
|
const schemaError = err instanceof Error ? err : new Error(String(err));
|
|
@@ -2901,6 +3000,7 @@ function AnalyticsProvider({
|
|
|
2901
3000
|
() => ({
|
|
2902
3001
|
client,
|
|
2903
3002
|
schema,
|
|
3003
|
+
dataSources,
|
|
2904
3004
|
isLoading,
|
|
2905
3005
|
error,
|
|
2906
3006
|
refetchSchema,
|
|
@@ -2908,7 +3008,7 @@ function AnalyticsProvider({
|
|
|
2908
3008
|
userId,
|
|
2909
3009
|
schemaName
|
|
2910
3010
|
}),
|
|
2911
|
-
[client, schema, isLoading, error, refetchSchema, tenantId, userId, schemaName]
|
|
3011
|
+
[client, schema, dataSources, isLoading, error, refetchSchema, tenantId, userId, schemaName]
|
|
2912
3012
|
);
|
|
2913
3013
|
const callbacks = useMemo(
|
|
2914
3014
|
() => ({
|
|
@@ -2929,22 +3029,44 @@ function useAnalytics() {
|
|
|
2929
3029
|
return context;
|
|
2930
3030
|
}
|
|
2931
3031
|
function useSchema() {
|
|
2932
|
-
const { schema, isLoading, error } = useAnalytics();
|
|
3032
|
+
const { schema, dataSources, isLoading, error } = useAnalytics();
|
|
2933
3033
|
const tables = useMemo(() => schema?.tables ?? [], [schema]);
|
|
2934
3034
|
const relationships = useMemo(() => schema?.relationships ?? [], [schema]);
|
|
3035
|
+
const dataSourceMap = useMemo(() => {
|
|
3036
|
+
const map = /* @__PURE__ */ new Map();
|
|
3037
|
+
for (const ds of dataSources) {
|
|
3038
|
+
map.set(ds.table, ds);
|
|
3039
|
+
}
|
|
3040
|
+
return map;
|
|
3041
|
+
}, [dataSources]);
|
|
2935
3042
|
const getTable = useCallback(
|
|
2936
3043
|
(name) => {
|
|
2937
3044
|
return tables.find((table) => table.name === name);
|
|
2938
3045
|
},
|
|
2939
3046
|
[tables]
|
|
2940
3047
|
);
|
|
3048
|
+
const getDisplayName = useCallback(
|
|
3049
|
+
(tableName) => {
|
|
3050
|
+
return dataSourceMap.get(tableName)?.title ?? tableName;
|
|
3051
|
+
},
|
|
3052
|
+
[dataSourceMap]
|
|
3053
|
+
);
|
|
3054
|
+
const getDescription = useCallback(
|
|
3055
|
+
(tableName) => {
|
|
3056
|
+
return dataSourceMap.get(tableName)?.subtitle ?? "";
|
|
3057
|
+
},
|
|
3058
|
+
[dataSourceMap]
|
|
3059
|
+
);
|
|
2941
3060
|
return {
|
|
2942
3061
|
schema,
|
|
2943
3062
|
tables,
|
|
2944
3063
|
relationships,
|
|
3064
|
+
dataSources,
|
|
2945
3065
|
isLoading,
|
|
2946
3066
|
error,
|
|
2947
|
-
getTable
|
|
3067
|
+
getTable,
|
|
3068
|
+
getDisplayName,
|
|
3069
|
+
getDescription
|
|
2948
3070
|
};
|
|
2949
3071
|
}
|
|
2950
3072
|
function queryEquals(a, b) {
|
|
@@ -3853,6 +3975,140 @@ function CrossFilterProvider({
|
|
|
3853
3975
|
function useCrossFilterOptional() {
|
|
3854
3976
|
return useContext(CrossFilterContext);
|
|
3855
3977
|
}
|
|
3978
|
+
|
|
3979
|
+
// src/hooks/useLLMStatus.ts
|
|
3980
|
+
function useLLMStatus() {
|
|
3981
|
+
const { client } = useAnalytics();
|
|
3982
|
+
const [status, setStatus] = useState({ enabled: false });
|
|
3983
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
3984
|
+
const [error, setError] = useState(null);
|
|
3985
|
+
useEffect(() => {
|
|
3986
|
+
if (!client) {
|
|
3987
|
+
setIsLoading(false);
|
|
3988
|
+
return;
|
|
3989
|
+
}
|
|
3990
|
+
let cancelled = false;
|
|
3991
|
+
client.getLLMStatus().then((result) => {
|
|
3992
|
+
if (!cancelled) {
|
|
3993
|
+
setStatus(result);
|
|
3994
|
+
setError(null);
|
|
3995
|
+
}
|
|
3996
|
+
}).catch((err) => {
|
|
3997
|
+
if (!cancelled) {
|
|
3998
|
+
setStatus({ enabled: false });
|
|
3999
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
4000
|
+
}
|
|
4001
|
+
}).finally(() => {
|
|
4002
|
+
if (!cancelled) setIsLoading(false);
|
|
4003
|
+
});
|
|
4004
|
+
return () => {
|
|
4005
|
+
cancelled = true;
|
|
4006
|
+
};
|
|
4007
|
+
}, [client]);
|
|
4008
|
+
return {
|
|
4009
|
+
enabled: status.enabled,
|
|
4010
|
+
provider: status.provider,
|
|
4011
|
+
model: status.model,
|
|
4012
|
+
isLoading,
|
|
4013
|
+
error
|
|
4014
|
+
};
|
|
4015
|
+
}
|
|
4016
|
+
function useLLMChat() {
|
|
4017
|
+
const { client } = useAnalytics();
|
|
4018
|
+
const [messages, setMessages] = useState([]);
|
|
4019
|
+
const [isStreaming, setIsStreaming] = useState(false);
|
|
4020
|
+
const [streamingContent, setStreamingContent] = useState("");
|
|
4021
|
+
const [suggestedSql, setSuggestedSql] = useState(null);
|
|
4022
|
+
const [error, setError] = useState(null);
|
|
4023
|
+
const abortRef = useRef(null);
|
|
4024
|
+
const isStreamingRef = useRef(false);
|
|
4025
|
+
const messagesRef = useRef([]);
|
|
4026
|
+
messagesRef.current = messages;
|
|
4027
|
+
const sendMessage = useCallback(
|
|
4028
|
+
async (message, currentSql) => {
|
|
4029
|
+
if (!client || isStreamingRef.current) return;
|
|
4030
|
+
abortRef.current?.abort();
|
|
4031
|
+
const controller = new AbortController();
|
|
4032
|
+
abortRef.current = controller;
|
|
4033
|
+
const userMsg = { role: "user", content: message };
|
|
4034
|
+
setMessages((prev) => [...prev, userMsg]);
|
|
4035
|
+
isStreamingRef.current = true;
|
|
4036
|
+
setIsStreaming(true);
|
|
4037
|
+
setStreamingContent("");
|
|
4038
|
+
setSuggestedSql(null);
|
|
4039
|
+
setError(null);
|
|
4040
|
+
let accumulatedText = "";
|
|
4041
|
+
let lastSql = null;
|
|
4042
|
+
try {
|
|
4043
|
+
const history = messagesRef.current.map((m) => ({
|
|
4044
|
+
role: m.role,
|
|
4045
|
+
content: m.content
|
|
4046
|
+
}));
|
|
4047
|
+
for await (const chunk of client.streamChat(
|
|
4048
|
+
message,
|
|
4049
|
+
history,
|
|
4050
|
+
currentSql,
|
|
4051
|
+
controller.signal
|
|
4052
|
+
)) {
|
|
4053
|
+
if (controller.signal.aborted) break;
|
|
4054
|
+
switch (chunk.type) {
|
|
4055
|
+
case "text":
|
|
4056
|
+
accumulatedText += chunk.content ?? "";
|
|
4057
|
+
setStreamingContent(accumulatedText);
|
|
4058
|
+
break;
|
|
4059
|
+
case "sql":
|
|
4060
|
+
lastSql = chunk.content ?? null;
|
|
4061
|
+
setSuggestedSql(lastSql);
|
|
4062
|
+
break;
|
|
4063
|
+
case "error":
|
|
4064
|
+
setError(chunk.content ?? "Unknown error");
|
|
4065
|
+
break;
|
|
4066
|
+
case "done":
|
|
4067
|
+
break;
|
|
4068
|
+
}
|
|
4069
|
+
}
|
|
4070
|
+
} catch (err) {
|
|
4071
|
+
if (err instanceof Error && err.name !== "AbortError") {
|
|
4072
|
+
setError(err.message);
|
|
4073
|
+
}
|
|
4074
|
+
} finally {
|
|
4075
|
+
isStreamingRef.current = false;
|
|
4076
|
+
setIsStreaming(false);
|
|
4077
|
+
if (accumulatedText) {
|
|
4078
|
+
const assistantMsg = {
|
|
4079
|
+
role: "assistant",
|
|
4080
|
+
content: accumulatedText
|
|
4081
|
+
};
|
|
4082
|
+
setMessages((prev) => [...prev, assistantMsg]);
|
|
4083
|
+
}
|
|
4084
|
+
setStreamingContent("");
|
|
4085
|
+
}
|
|
4086
|
+
},
|
|
4087
|
+
[client]
|
|
4088
|
+
);
|
|
4089
|
+
useEffect(() => {
|
|
4090
|
+
return () => {
|
|
4091
|
+
abortRef.current?.abort();
|
|
4092
|
+
};
|
|
4093
|
+
}, []);
|
|
4094
|
+
const clearHistory = useCallback(() => {
|
|
4095
|
+
abortRef.current?.abort();
|
|
4096
|
+
setMessages([]);
|
|
4097
|
+
setStreamingContent("");
|
|
4098
|
+
setSuggestedSql(null);
|
|
4099
|
+
setError(null);
|
|
4100
|
+
setIsStreaming(false);
|
|
4101
|
+
}, []);
|
|
4102
|
+
return {
|
|
4103
|
+
messages,
|
|
4104
|
+
isStreaming,
|
|
4105
|
+
streamingContent,
|
|
4106
|
+
suggestedSql,
|
|
4107
|
+
sendMessage,
|
|
4108
|
+
clearHistory,
|
|
4109
|
+
error
|
|
4110
|
+
};
|
|
4111
|
+
}
|
|
3856
4112
|
var nodeStyles = {
|
|
3857
4113
|
display: "flex",
|
|
3858
4114
|
alignItems: "center",
|
|
@@ -4315,6 +4571,7 @@ function SchemaExplorer({
|
|
|
4315
4571
|
selectedColumns = [],
|
|
4316
4572
|
searchable = true,
|
|
4317
4573
|
collapsible = true,
|
|
4574
|
+
headerAction,
|
|
4318
4575
|
className,
|
|
4319
4576
|
style
|
|
4320
4577
|
}) {
|
|
@@ -4350,10 +4607,12 @@ function SchemaExplorer({
|
|
|
4350
4607
|
style: { ...containerStyles4, ...style },
|
|
4351
4608
|
role: "tree",
|
|
4352
4609
|
"aria-label": "Database schema",
|
|
4610
|
+
"data-testid": "schema-explorer-root",
|
|
4353
4611
|
children: [
|
|
4354
4612
|
/* @__PURE__ */ jsxs("div", { style: headerStyles2, children: [
|
|
4355
4613
|
/* @__PURE__ */ jsx(Icon, { name: "table", size: 16, style: { color: "var(--prismiq-color-primary)" } }),
|
|
4356
|
-
/* @__PURE__ */ jsx("span", { style: titleStyles2, children: "Schema Explorer" })
|
|
4614
|
+
/* @__PURE__ */ jsx("span", { style: { ...titleStyles2, flex: 1 }, children: "Schema Explorer" }),
|
|
4615
|
+
headerAction
|
|
4357
4616
|
] }),
|
|
4358
4617
|
searchable && /* @__PURE__ */ jsx("div", { style: searchContainerStyles, children: /* @__PURE__ */ jsx(
|
|
4359
4618
|
Input,
|
|
@@ -4362,7 +4621,8 @@ function SchemaExplorer({
|
|
|
4362
4621
|
placeholder: "Search tables and columns...",
|
|
4363
4622
|
value: searchQuery,
|
|
4364
4623
|
onChange: (e) => setSearchQuery(e.target.value),
|
|
4365
|
-
style: { width: "100%" }
|
|
4624
|
+
style: { width: "100%" },
|
|
4625
|
+
"data-testid": "schema-explorer-search"
|
|
4366
4626
|
}
|
|
4367
4627
|
) }),
|
|
4368
4628
|
/* @__PURE__ */ jsx("div", { style: treeContainerStyles, children: isLoading ? /* @__PURE__ */ jsx(LoadingSkeleton, {}) : error ? /* @__PURE__ */ jsxs("div", { style: errorStyles2, children: [
|
|
@@ -4833,6 +5093,29 @@ var containerStyles7 = {
|
|
|
4833
5093
|
gap: "var(--prismiq-spacing-xs)",
|
|
4834
5094
|
flex: 1
|
|
4835
5095
|
};
|
|
5096
|
+
var comboboxContainerStyles = {
|
|
5097
|
+
position: "relative",
|
|
5098
|
+
flex: 1
|
|
5099
|
+
};
|
|
5100
|
+
var dropdownStyles2 = {
|
|
5101
|
+
position: "fixed",
|
|
5102
|
+
backgroundColor: "var(--prismiq-color-background)",
|
|
5103
|
+
border: "1px solid var(--prismiq-color-border)",
|
|
5104
|
+
borderRadius: "var(--prismiq-radius-md)",
|
|
5105
|
+
boxShadow: "var(--prismiq-shadow-md)",
|
|
5106
|
+
zIndex: 1e3,
|
|
5107
|
+
maxHeight: "200px",
|
|
5108
|
+
overflow: "auto"
|
|
5109
|
+
};
|
|
5110
|
+
var optionStyles2 = {
|
|
5111
|
+
padding: "var(--prismiq-spacing-sm) var(--prismiq-spacing-md)",
|
|
5112
|
+
cursor: "pointer",
|
|
5113
|
+
fontSize: "var(--prismiq-font-size-sm)",
|
|
5114
|
+
transition: "background-color 0.1s"
|
|
5115
|
+
};
|
|
5116
|
+
var optionHoverStyles2 = {
|
|
5117
|
+
backgroundColor: "var(--prismiq-color-surface-hover)"
|
|
5118
|
+
};
|
|
4836
5119
|
function parseValue(value, dataType) {
|
|
4837
5120
|
if (!value) return void 0;
|
|
4838
5121
|
const type = dataType?.toLowerCase() ?? "";
|
|
@@ -4863,15 +5146,270 @@ function getInputType(dataType) {
|
|
|
4863
5146
|
}
|
|
4864
5147
|
return "text";
|
|
4865
5148
|
}
|
|
5149
|
+
function isMultiValueOperator(op) {
|
|
5150
|
+
return op === "in_" || op === "not_in" || op === "in_or_null";
|
|
5151
|
+
}
|
|
5152
|
+
var tagContainerStyles = {
|
|
5153
|
+
display: "flex",
|
|
5154
|
+
flexWrap: "wrap",
|
|
5155
|
+
alignItems: "center",
|
|
5156
|
+
gap: "4px",
|
|
5157
|
+
padding: "4px 8px",
|
|
5158
|
+
border: "1px solid var(--prismiq-color-border)",
|
|
5159
|
+
borderRadius: "var(--prismiq-radius-sm)",
|
|
5160
|
+
backgroundColor: "var(--prismiq-color-background)",
|
|
5161
|
+
minHeight: "32px",
|
|
5162
|
+
cursor: "text",
|
|
5163
|
+
flex: 1
|
|
5164
|
+
};
|
|
5165
|
+
var tagStyles = {
|
|
5166
|
+
display: "inline-flex",
|
|
5167
|
+
alignItems: "center",
|
|
5168
|
+
gap: "4px",
|
|
5169
|
+
padding: "1px 6px",
|
|
5170
|
+
backgroundColor: "var(--prismiq-color-surface)",
|
|
5171
|
+
border: "1px solid var(--prismiq-color-border)",
|
|
5172
|
+
borderRadius: "var(--prismiq-radius-sm)",
|
|
5173
|
+
fontSize: "var(--prismiq-font-size-sm)",
|
|
5174
|
+
lineHeight: "20px",
|
|
5175
|
+
whiteSpace: "nowrap"
|
|
5176
|
+
};
|
|
5177
|
+
var tagRemoveStyles = {
|
|
5178
|
+
display: "inline-flex",
|
|
5179
|
+
alignItems: "center",
|
|
5180
|
+
justifyContent: "center",
|
|
5181
|
+
width: "14px",
|
|
5182
|
+
height: "14px",
|
|
5183
|
+
border: "none",
|
|
5184
|
+
background: "none",
|
|
5185
|
+
cursor: "pointer",
|
|
5186
|
+
padding: 0,
|
|
5187
|
+
fontSize: "12px",
|
|
5188
|
+
lineHeight: 1,
|
|
5189
|
+
color: "var(--prismiq-color-text-muted)",
|
|
5190
|
+
borderRadius: "50%"
|
|
5191
|
+
};
|
|
5192
|
+
var tagInputStyles = {
|
|
5193
|
+
border: "none",
|
|
5194
|
+
outline: "none",
|
|
5195
|
+
background: "none",
|
|
5196
|
+
flex: 1,
|
|
5197
|
+
minWidth: "80px",
|
|
5198
|
+
fontSize: "var(--prismiq-font-size-sm)",
|
|
5199
|
+
padding: "2px 0",
|
|
5200
|
+
color: "var(--prismiq-color-text)"
|
|
5201
|
+
};
|
|
5202
|
+
var multiOptionCheckStyles = {
|
|
5203
|
+
marginRight: "6px",
|
|
5204
|
+
color: "var(--prismiq-color-primary)",
|
|
5205
|
+
fontWeight: 700,
|
|
5206
|
+
fontSize: "12px"
|
|
5207
|
+
};
|
|
4866
5208
|
function FilterValueInput({
|
|
4867
5209
|
operator,
|
|
4868
5210
|
value,
|
|
4869
5211
|
onChange,
|
|
4870
5212
|
dataType,
|
|
4871
5213
|
disabled = false,
|
|
4872
|
-
className
|
|
5214
|
+
className,
|
|
5215
|
+
tableName,
|
|
5216
|
+
columnName
|
|
4873
5217
|
}) {
|
|
5218
|
+
const { client } = useAnalytics();
|
|
4874
5219
|
const inputType = getInputType(dataType);
|
|
5220
|
+
const isMulti = isMultiValueOperator(operator);
|
|
5221
|
+
const [sampleValues, setSampleValues] = useState([]);
|
|
5222
|
+
const [isLoadingValues, setIsLoadingValues] = useState(false);
|
|
5223
|
+
const fetchedRef = useRef(null);
|
|
5224
|
+
const fetchSeqRef = useRef(0);
|
|
5225
|
+
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
|
5226
|
+
const [highlightedIndex, setHighlightedIndex] = useState(-1);
|
|
5227
|
+
const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0, width: 0 });
|
|
5228
|
+
const inputRef = useRef(null);
|
|
5229
|
+
const dropdownRef = useRef(null);
|
|
5230
|
+
const containerRef = useRef(null);
|
|
5231
|
+
const [multiInputText, setMultiInputText] = useState("");
|
|
5232
|
+
useEffect(() => {
|
|
5233
|
+
setMultiInputText("");
|
|
5234
|
+
}, [operator]);
|
|
5235
|
+
useEffect(() => {
|
|
5236
|
+
if (!tableName || !columnName || !client) {
|
|
5237
|
+
setSampleValues([]);
|
|
5238
|
+
setIsLoadingValues(false);
|
|
5239
|
+
fetchedRef.current = null;
|
|
5240
|
+
return;
|
|
5241
|
+
}
|
|
5242
|
+
const fetchKey = `${tableName}.${columnName}`;
|
|
5243
|
+
if (fetchedRef.current === fetchKey) return;
|
|
5244
|
+
const fetchSeq = ++fetchSeqRef.current;
|
|
5245
|
+
const fetchSamples = async () => {
|
|
5246
|
+
setIsLoadingValues(true);
|
|
5247
|
+
try {
|
|
5248
|
+
const values = await client.getColumnSample(tableName, columnName, 100);
|
|
5249
|
+
const stringValues = values.filter((v) => v !== null && v !== void 0).map((v) => String(v));
|
|
5250
|
+
if (fetchSeqRef.current !== fetchSeq) return;
|
|
5251
|
+
setSampleValues(stringValues);
|
|
5252
|
+
fetchedRef.current = fetchKey;
|
|
5253
|
+
} catch (err) {
|
|
5254
|
+
if (fetchSeqRef.current !== fetchSeq) return;
|
|
5255
|
+
console.error("Failed to fetch sample values:", err);
|
|
5256
|
+
setSampleValues([]);
|
|
5257
|
+
fetchedRef.current = null;
|
|
5258
|
+
} finally {
|
|
5259
|
+
if (fetchSeqRef.current === fetchSeq) {
|
|
5260
|
+
setIsLoadingValues(false);
|
|
5261
|
+
}
|
|
5262
|
+
}
|
|
5263
|
+
};
|
|
5264
|
+
fetchSamples();
|
|
5265
|
+
}, [client, tableName, columnName]);
|
|
5266
|
+
const selectedValues = isMulti && Array.isArray(value) ? value.filter((v) => v !== null && v !== void 0).map((v) => String(v)) : [];
|
|
5267
|
+
const currentValueStr = isMulti ? multiInputText : formatValue(value);
|
|
5268
|
+
const filteredOptions = isMulti ? sampleValues.filter(
|
|
5269
|
+
(v) => v.toLowerCase().includes(multiInputText.toLowerCase()) && !selectedValues.includes(v)
|
|
5270
|
+
) : sampleValues.filter(
|
|
5271
|
+
(v) => v.toLowerCase().includes(currentValueStr.toLowerCase())
|
|
5272
|
+
);
|
|
5273
|
+
const updateDropdownPosition = useCallback(() => {
|
|
5274
|
+
const el = isMulti ? containerRef.current : inputRef.current;
|
|
5275
|
+
if (el) {
|
|
5276
|
+
const rect = el.getBoundingClientRect();
|
|
5277
|
+
setDropdownPosition({
|
|
5278
|
+
top: rect.bottom + 4,
|
|
5279
|
+
left: rect.left,
|
|
5280
|
+
width: rect.width
|
|
5281
|
+
});
|
|
5282
|
+
}
|
|
5283
|
+
}, [isMulti]);
|
|
5284
|
+
useEffect(() => {
|
|
5285
|
+
const handleClickOutside = (event) => {
|
|
5286
|
+
const target = event.target;
|
|
5287
|
+
const isInsideContainer = containerRef.current?.contains(target);
|
|
5288
|
+
const isInsideDropdown = dropdownRef.current?.contains(target);
|
|
5289
|
+
if (!isInsideContainer && !isInsideDropdown) {
|
|
5290
|
+
setIsDropdownOpen(false);
|
|
5291
|
+
}
|
|
5292
|
+
};
|
|
5293
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
5294
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
5295
|
+
}, []);
|
|
5296
|
+
const handleInputFocus = useCallback(() => {
|
|
5297
|
+
if (sampleValues.length > 0) {
|
|
5298
|
+
updateDropdownPosition();
|
|
5299
|
+
setIsDropdownOpen(true);
|
|
5300
|
+
setHighlightedIndex(-1);
|
|
5301
|
+
}
|
|
5302
|
+
}, [sampleValues.length, updateDropdownPosition]);
|
|
5303
|
+
const handleOptionSelect = useCallback(
|
|
5304
|
+
(optionValue) => {
|
|
5305
|
+
onChange(parseValue(optionValue, dataType));
|
|
5306
|
+
setIsDropdownOpen(false);
|
|
5307
|
+
inputRef.current?.blur();
|
|
5308
|
+
},
|
|
5309
|
+
[onChange, dataType]
|
|
5310
|
+
);
|
|
5311
|
+
const addMultiValue = useCallback(
|
|
5312
|
+
(val) => {
|
|
5313
|
+
const trimmed = val.trim();
|
|
5314
|
+
if (!trimmed) return;
|
|
5315
|
+
if (selectedValues.includes(trimmed)) return;
|
|
5316
|
+
const newValues = [...selectedValues, trimmed].map((v) => parseValue(v, dataType));
|
|
5317
|
+
onChange(newValues);
|
|
5318
|
+
setMultiInputText("");
|
|
5319
|
+
},
|
|
5320
|
+
[selectedValues, onChange, dataType]
|
|
5321
|
+
);
|
|
5322
|
+
const removeMultiValue = useCallback(
|
|
5323
|
+
(val) => {
|
|
5324
|
+
const newValues = selectedValues.filter((v) => v !== val).map((v) => parseValue(v, dataType));
|
|
5325
|
+
onChange(newValues.length > 0 ? newValues : []);
|
|
5326
|
+
},
|
|
5327
|
+
[selectedValues, onChange, dataType]
|
|
5328
|
+
);
|
|
5329
|
+
const handleMultiOptionSelect = useCallback(
|
|
5330
|
+
(optionValue) => {
|
|
5331
|
+
if (selectedValues.includes(optionValue)) {
|
|
5332
|
+
removeMultiValue(optionValue);
|
|
5333
|
+
} else {
|
|
5334
|
+
addMultiValue(optionValue);
|
|
5335
|
+
}
|
|
5336
|
+
setMultiInputText("");
|
|
5337
|
+
updateDropdownPosition();
|
|
5338
|
+
inputRef.current?.focus();
|
|
5339
|
+
},
|
|
5340
|
+
[selectedValues, addMultiValue, removeMultiValue, updateDropdownPosition]
|
|
5341
|
+
);
|
|
5342
|
+
const handleMultiInputKeyDown = useCallback(
|
|
5343
|
+
(e) => {
|
|
5344
|
+
if (e.key === "Backspace" && multiInputText === "" && selectedValues.length > 0) {
|
|
5345
|
+
const lastVal = selectedValues[selectedValues.length - 1];
|
|
5346
|
+
if (lastVal !== void 0) removeMultiValue(lastVal);
|
|
5347
|
+
return;
|
|
5348
|
+
}
|
|
5349
|
+
if (isDropdownOpen && filteredOptions.length > 0) {
|
|
5350
|
+
switch (e.key) {
|
|
5351
|
+
case "ArrowDown":
|
|
5352
|
+
e.preventDefault();
|
|
5353
|
+
setHighlightedIndex((prev) => Math.min(prev + 1, filteredOptions.length - 1));
|
|
5354
|
+
return;
|
|
5355
|
+
case "ArrowUp":
|
|
5356
|
+
e.preventDefault();
|
|
5357
|
+
setHighlightedIndex((prev) => Math.max(prev - 1, 0));
|
|
5358
|
+
return;
|
|
5359
|
+
case "Enter":
|
|
5360
|
+
e.preventDefault();
|
|
5361
|
+
if (highlightedIndex >= 0 && filteredOptions[highlightedIndex]) {
|
|
5362
|
+
handleMultiOptionSelect(filteredOptions[highlightedIndex]);
|
|
5363
|
+
} else if (multiInputText.trim()) {
|
|
5364
|
+
addMultiValue(multiInputText);
|
|
5365
|
+
}
|
|
5366
|
+
return;
|
|
5367
|
+
case "Escape":
|
|
5368
|
+
setIsDropdownOpen(false);
|
|
5369
|
+
return;
|
|
5370
|
+
}
|
|
5371
|
+
}
|
|
5372
|
+
if (e.key === "," || e.key === "Enter") {
|
|
5373
|
+
e.preventDefault();
|
|
5374
|
+
addMultiValue(multiInputText);
|
|
5375
|
+
}
|
|
5376
|
+
},
|
|
5377
|
+
[
|
|
5378
|
+
multiInputText,
|
|
5379
|
+
selectedValues,
|
|
5380
|
+
isDropdownOpen,
|
|
5381
|
+
filteredOptions,
|
|
5382
|
+
highlightedIndex,
|
|
5383
|
+
addMultiValue,
|
|
5384
|
+
removeMultiValue,
|
|
5385
|
+
handleMultiOptionSelect
|
|
5386
|
+
]
|
|
5387
|
+
);
|
|
5388
|
+
const handleSingleKeyDown = useCallback(
|
|
5389
|
+
(e) => {
|
|
5390
|
+
if (!isDropdownOpen || filteredOptions.length === 0) return;
|
|
5391
|
+
switch (e.key) {
|
|
5392
|
+
case "ArrowDown":
|
|
5393
|
+
e.preventDefault();
|
|
5394
|
+
setHighlightedIndex((prev) => Math.min(prev + 1, filteredOptions.length - 1));
|
|
5395
|
+
break;
|
|
5396
|
+
case "ArrowUp":
|
|
5397
|
+
e.preventDefault();
|
|
5398
|
+
setHighlightedIndex((prev) => Math.max(prev - 1, 0));
|
|
5399
|
+
break;
|
|
5400
|
+
case "Enter":
|
|
5401
|
+
e.preventDefault();
|
|
5402
|
+
if (highlightedIndex >= 0 && filteredOptions[highlightedIndex]) {
|
|
5403
|
+
handleOptionSelect(filteredOptions[highlightedIndex]);
|
|
5404
|
+
}
|
|
5405
|
+
break;
|
|
5406
|
+
case "Escape":
|
|
5407
|
+
setIsDropdownOpen(false);
|
|
5408
|
+
break;
|
|
5409
|
+
}
|
|
5410
|
+
},
|
|
5411
|
+
[isDropdownOpen, filteredOptions, highlightedIndex, handleOptionSelect]
|
|
5412
|
+
);
|
|
4875
5413
|
if (operator === "is_null" || operator === "is_not_null") {
|
|
4876
5414
|
return /* @__PURE__ */ jsx(Fragment, {});
|
|
4877
5415
|
}
|
|
@@ -4911,36 +5449,163 @@ function FilterValueInput({
|
|
|
4911
5449
|
)
|
|
4912
5450
|
] });
|
|
4913
5451
|
}
|
|
4914
|
-
if (
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
5452
|
+
if (isMulti) {
|
|
5453
|
+
return /* @__PURE__ */ jsx("div", { className, style: containerStyles7, children: /* @__PURE__ */ jsxs("div", { ref: containerRef, style: comboboxContainerStyles, children: [
|
|
5454
|
+
/* @__PURE__ */ jsxs(
|
|
5455
|
+
"div",
|
|
5456
|
+
{
|
|
5457
|
+
"data-testid": "filter-tag-container",
|
|
5458
|
+
style: tagContainerStyles,
|
|
5459
|
+
onClick: () => inputRef.current?.focus(),
|
|
5460
|
+
children: [
|
|
5461
|
+
selectedValues.map((val) => /* @__PURE__ */ jsxs("span", { "data-testid": `filter-tag-${val}`, style: tagStyles, children: [
|
|
5462
|
+
val,
|
|
5463
|
+
/* @__PURE__ */ jsx(
|
|
5464
|
+
"button",
|
|
5465
|
+
{
|
|
5466
|
+
type: "button",
|
|
5467
|
+
"data-testid": `filter-tag-remove-${val}`,
|
|
5468
|
+
style: tagRemoveStyles,
|
|
5469
|
+
onClick: (e) => {
|
|
5470
|
+
e.stopPropagation();
|
|
5471
|
+
removeMultiValue(val);
|
|
5472
|
+
},
|
|
5473
|
+
tabIndex: -1,
|
|
5474
|
+
children: "\xD7"
|
|
5475
|
+
}
|
|
5476
|
+
)
|
|
5477
|
+
] }, val)),
|
|
5478
|
+
/* @__PURE__ */ jsx(
|
|
5479
|
+
"input",
|
|
5480
|
+
{
|
|
5481
|
+
ref: inputRef,
|
|
5482
|
+
"data-testid": "filter-multi-input",
|
|
5483
|
+
type: "text",
|
|
5484
|
+
placeholder: selectedValues.length === 0 ? isLoadingValues ? "Loading..." : "Type or select values" : "",
|
|
5485
|
+
value: multiInputText,
|
|
5486
|
+
disabled: disabled || isLoadingValues,
|
|
5487
|
+
onChange: (e) => {
|
|
5488
|
+
setMultiInputText(e.target.value);
|
|
5489
|
+
if (sampleValues.length > 0) {
|
|
5490
|
+
updateDropdownPosition();
|
|
5491
|
+
setIsDropdownOpen(true);
|
|
5492
|
+
setHighlightedIndex(-1);
|
|
5493
|
+
}
|
|
5494
|
+
},
|
|
5495
|
+
onFocus: handleInputFocus,
|
|
5496
|
+
onKeyDown: handleMultiInputKeyDown,
|
|
5497
|
+
style: tagInputStyles
|
|
5498
|
+
}
|
|
5499
|
+
)
|
|
5500
|
+
]
|
|
5501
|
+
}
|
|
5502
|
+
),
|
|
5503
|
+
selectedValues.length === 0 && !multiInputText && /* @__PURE__ */ jsxs("div", { "data-testid": "filter-multi-hint", style: {
|
|
5504
|
+
fontSize: "11px",
|
|
5505
|
+
color: "var(--prismiq-color-text-muted)",
|
|
5506
|
+
marginTop: "2px",
|
|
5507
|
+
paddingLeft: "2px"
|
|
5508
|
+
}, children: [
|
|
5509
|
+
"Press ",
|
|
5510
|
+
/* @__PURE__ */ jsx("kbd", { style: { padding: "0 3px", border: "1px solid var(--prismiq-color-border)", borderRadius: "3px", fontSize: "10px" }, children: "," }),
|
|
5511
|
+
" or ",
|
|
5512
|
+
/* @__PURE__ */ jsx("kbd", { style: { padding: "0 3px", border: "1px solid var(--prismiq-color-border)", borderRadius: "3px", fontSize: "10px" }, children: "Enter" }),
|
|
5513
|
+
" to add values"
|
|
5514
|
+
] }),
|
|
5515
|
+
isDropdownOpen && filteredOptions.length > 0 && typeof document !== "undefined" && createPortal(
|
|
5516
|
+
/* @__PURE__ */ jsx(
|
|
5517
|
+
"div",
|
|
5518
|
+
{
|
|
5519
|
+
ref: dropdownRef,
|
|
5520
|
+
"data-testid": "filter-dropdown",
|
|
5521
|
+
style: {
|
|
5522
|
+
...dropdownStyles2,
|
|
5523
|
+
top: dropdownPosition.top,
|
|
5524
|
+
left: dropdownPosition.left,
|
|
5525
|
+
width: dropdownPosition.width
|
|
5526
|
+
},
|
|
5527
|
+
children: filteredOptions.map((optionValue, index) => {
|
|
5528
|
+
const isSelected = selectedValues.includes(optionValue);
|
|
5529
|
+
return /* @__PURE__ */ jsxs(
|
|
5530
|
+
"div",
|
|
5531
|
+
{
|
|
5532
|
+
"data-testid": `filter-option-${index}`,
|
|
5533
|
+
onClick: () => handleMultiOptionSelect(optionValue),
|
|
5534
|
+
onMouseEnter: () => setHighlightedIndex(index),
|
|
5535
|
+
style: {
|
|
5536
|
+
...optionStyles2,
|
|
5537
|
+
...index === highlightedIndex ? optionHoverStyles2 : {},
|
|
5538
|
+
...isSelected ? { backgroundColor: "var(--prismiq-color-surface)", fontWeight: 500 } : {}
|
|
5539
|
+
},
|
|
5540
|
+
children: [
|
|
5541
|
+
/* @__PURE__ */ jsx("span", { style: multiOptionCheckStyles, children: isSelected ? "\u2713" : "\u2003" }),
|
|
5542
|
+
optionValue
|
|
5543
|
+
]
|
|
5544
|
+
},
|
|
5545
|
+
`${optionValue}-${index}`
|
|
5546
|
+
);
|
|
5547
|
+
})
|
|
5548
|
+
}
|
|
5549
|
+
),
|
|
5550
|
+
document.body
|
|
5551
|
+
)
|
|
5552
|
+
] }) });
|
|
5553
|
+
}
|
|
5554
|
+
return /* @__PURE__ */ jsx("div", { className, style: containerStyles7, children: /* @__PURE__ */ jsxs("div", { ref: containerRef, style: comboboxContainerStyles, children: [
|
|
5555
|
+
/* @__PURE__ */ jsx(
|
|
4920
5556
|
Input,
|
|
4921
5557
|
{
|
|
5558
|
+
ref: inputRef,
|
|
5559
|
+
"data-testid": "filter-single-input",
|
|
4922
5560
|
inputSize: "sm",
|
|
4923
|
-
type:
|
|
4924
|
-
placeholder:
|
|
4925
|
-
value:
|
|
4926
|
-
disabled,
|
|
4927
|
-
onChange:
|
|
4928
|
-
|
|
5561
|
+
type: inputType,
|
|
5562
|
+
placeholder: isLoadingValues ? "Loading..." : "Type or select value",
|
|
5563
|
+
value: currentValueStr,
|
|
5564
|
+
disabled: disabled || isLoadingValues,
|
|
5565
|
+
onChange: (e) => {
|
|
5566
|
+
onChange(parseValue(e.target.value, dataType));
|
|
5567
|
+
if (sampleValues.length > 0) {
|
|
5568
|
+
updateDropdownPosition();
|
|
5569
|
+
setIsDropdownOpen(true);
|
|
5570
|
+
}
|
|
5571
|
+
},
|
|
5572
|
+
onFocus: handleInputFocus,
|
|
5573
|
+
onKeyDown: handleSingleKeyDown,
|
|
5574
|
+
style: { width: "100%" }
|
|
4929
5575
|
}
|
|
4930
|
-
)
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
5576
|
+
),
|
|
5577
|
+
isDropdownOpen && filteredOptions.length > 0 && typeof document !== "undefined" && createPortal(
|
|
5578
|
+
/* @__PURE__ */ jsx(
|
|
5579
|
+
"div",
|
|
5580
|
+
{
|
|
5581
|
+
ref: dropdownRef,
|
|
5582
|
+
"data-testid": "filter-dropdown",
|
|
5583
|
+
style: {
|
|
5584
|
+
...dropdownStyles2,
|
|
5585
|
+
top: dropdownPosition.top,
|
|
5586
|
+
left: dropdownPosition.left,
|
|
5587
|
+
width: dropdownPosition.width
|
|
5588
|
+
},
|
|
5589
|
+
children: filteredOptions.map((optionValue, index) => /* @__PURE__ */ jsx(
|
|
5590
|
+
"div",
|
|
5591
|
+
{
|
|
5592
|
+
"data-testid": `filter-option-${index}`,
|
|
5593
|
+
onClick: () => handleOptionSelect(optionValue),
|
|
5594
|
+
onMouseEnter: () => setHighlightedIndex(index),
|
|
5595
|
+
style: {
|
|
5596
|
+
...optionStyles2,
|
|
5597
|
+
...index === highlightedIndex ? optionHoverStyles2 : {},
|
|
5598
|
+
...optionValue === currentValueStr ? { backgroundColor: "var(--prismiq-color-surface)", fontWeight: 500 } : {}
|
|
5599
|
+
},
|
|
5600
|
+
children: optionValue
|
|
5601
|
+
},
|
|
5602
|
+
`${optionValue}-${index}`
|
|
5603
|
+
))
|
|
5604
|
+
}
|
|
5605
|
+
),
|
|
5606
|
+
document.body
|
|
5607
|
+
)
|
|
5608
|
+
] }) });
|
|
4944
5609
|
}
|
|
4945
5610
|
var rowStyles = {
|
|
4946
5611
|
display: "flex",
|
|
@@ -5033,13 +5698,16 @@ function FilterRow({
|
|
|
5033
5698
|
});
|
|
5034
5699
|
return options;
|
|
5035
5700
|
}, [tables, schema]);
|
|
5701
|
+
const currentTable = useMemo(
|
|
5702
|
+
() => tables.find((t) => t.id === filter.table_id),
|
|
5703
|
+
[tables, filter.table_id]
|
|
5704
|
+
);
|
|
5036
5705
|
const currentColumnSchema = useMemo(() => {
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
const tableSchema = schema.tables.find((t) => t.name === table.name);
|
|
5706
|
+
if (!currentTable) return void 0;
|
|
5707
|
+
const tableSchema = schema.tables.find((t) => t.name === currentTable.name);
|
|
5040
5708
|
if (!tableSchema) return void 0;
|
|
5041
5709
|
return tableSchema.columns.find((c) => c.name === filter.column);
|
|
5042
|
-
}, [
|
|
5710
|
+
}, [currentTable, schema, filter.column]);
|
|
5043
5711
|
const operatorOptions = useMemo(
|
|
5044
5712
|
() => getOperatorsForType(currentColumnSchema?.data_type),
|
|
5045
5713
|
[currentColumnSchema]
|
|
@@ -5100,7 +5768,9 @@ function FilterRow({
|
|
|
5100
5768
|
operator: filter.operator,
|
|
5101
5769
|
value: filter.value,
|
|
5102
5770
|
onChange: handleValueChange,
|
|
5103
|
-
dataType: currentColumnSchema?.data_type
|
|
5771
|
+
dataType: currentColumnSchema?.data_type,
|
|
5772
|
+
tableName: currentTable?.name,
|
|
5773
|
+
columnName: filter.column
|
|
5104
5774
|
}
|
|
5105
5775
|
) }),
|
|
5106
5776
|
/* @__PURE__ */ jsx(
|
|
@@ -8416,7 +9086,14 @@ function CustomSQLEditor({
|
|
|
8416
9086
|
style
|
|
8417
9087
|
}) {
|
|
8418
9088
|
const [sql, setSql] = useState(initialSql);
|
|
9089
|
+
const prevInitialSqlRef = useRef(initialSql);
|
|
8419
9090
|
const [isFocused, setIsFocused] = useState(false);
|
|
9091
|
+
useEffect(() => {
|
|
9092
|
+
if (initialSql !== prevInitialSqlRef.current) {
|
|
9093
|
+
prevInitialSqlRef.current = initialSql;
|
|
9094
|
+
setSql(initialSql);
|
|
9095
|
+
}
|
|
9096
|
+
}, [initialSql]);
|
|
8420
9097
|
const [executeEnabled, setExecuteEnabled] = useState(false);
|
|
8421
9098
|
const [lastExecutedSql, setLastExecutedSql] = useState(null);
|
|
8422
9099
|
const {
|
|
@@ -8462,7 +9139,7 @@ function CustomSQLEditor({
|
|
|
8462
9139
|
...buttonStyles,
|
|
8463
9140
|
...canExecute ? {} : buttonDisabledStyles
|
|
8464
9141
|
};
|
|
8465
|
-
return /* @__PURE__ */ jsxs("div", { className, style: { ...containerStyles17, ...style }, children: [
|
|
9142
|
+
return /* @__PURE__ */ jsxs("div", { className, style: { ...containerStyles17, ...style }, "data-testid": "custom-sql-editor", children: [
|
|
8466
9143
|
/* @__PURE__ */ jsxs("div", { style: editorWrapperStyles, children: [
|
|
8467
9144
|
/* @__PURE__ */ jsx(
|
|
8468
9145
|
"textarea",
|
|
@@ -8476,7 +9153,8 @@ function CustomSQLEditor({
|
|
|
8476
9153
|
style: mergedTextareaStyles,
|
|
8477
9154
|
spellCheck: false,
|
|
8478
9155
|
autoComplete: "off",
|
|
8479
|
-
autoCapitalize: "off"
|
|
9156
|
+
autoCapitalize: "off",
|
|
9157
|
+
"data-testid": "custom-sql-textarea"
|
|
8480
9158
|
}
|
|
8481
9159
|
),
|
|
8482
9160
|
validation && !validation.valid && /* @__PURE__ */ jsxs("div", { style: validationErrorStyles, children: [
|
|
@@ -8500,6 +9178,7 @@ function CustomSQLEditor({
|
|
|
8500
9178
|
disabled: !canExecute,
|
|
8501
9179
|
style: mergedButtonStyles,
|
|
8502
9180
|
type: "button",
|
|
9181
|
+
"data-testid": "custom-sql-run-button",
|
|
8503
9182
|
children: [
|
|
8504
9183
|
isLoading ? "Executing..." : "Run Query",
|
|
8505
9184
|
/* @__PURE__ */ jsx("span", { style: { fontSize: "11px", opacity: 0.7 }, children: "(Cmd+Enter)" })
|
|
@@ -8541,13 +9220,14 @@ function TableSelector({
|
|
|
8541
9220
|
className
|
|
8542
9221
|
}) {
|
|
8543
9222
|
const { theme } = useTheme();
|
|
9223
|
+
const { getDisplayName } = useSchema();
|
|
8544
9224
|
const availableTableOptions = useMemo(() => {
|
|
8545
9225
|
const selectedNames = new Set(tables.map((t) => t.name));
|
|
8546
9226
|
return schema.tables.filter((t) => !selectedNames.has(t.name)).map((t) => ({
|
|
8547
9227
|
value: t.name,
|
|
8548
|
-
label:
|
|
9228
|
+
label: getDisplayName(t.name)
|
|
8549
9229
|
}));
|
|
8550
|
-
}, [schema.tables, tables]);
|
|
9230
|
+
}, [schema.tables, tables, getDisplayName]);
|
|
8551
9231
|
const suggestedTables = useMemo(() => {
|
|
8552
9232
|
if (!showRelationships || tables.length === 0) return [];
|
|
8553
9233
|
const selectedNames = new Set(tables.map((t) => t.name));
|
|
@@ -8649,7 +9329,7 @@ function TableSelector({
|
|
|
8649
9329
|
return /* @__PURE__ */ jsxs("div", { className, style: containerStyle, children: [
|
|
8650
9330
|
tables.length > 0 && /* @__PURE__ */ jsx("div", { style: selectedTablesStyle, children: tables.map((table, index) => /* @__PURE__ */ jsxs("div", { style: tableChipStyle, children: [
|
|
8651
9331
|
/* @__PURE__ */ jsx(Icon, { name: "table", size: 14 }),
|
|
8652
|
-
/* @__PURE__ */ jsx("span", { children: table.name }),
|
|
9332
|
+
/* @__PURE__ */ jsx("span", { children: getDisplayName(table.name) }),
|
|
8653
9333
|
index === 0 && /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "default", children: "primary" }),
|
|
8654
9334
|
tables.length > 1 && /* @__PURE__ */ jsx(
|
|
8655
9335
|
"button",
|
|
@@ -8657,7 +9337,7 @@ function TableSelector({
|
|
|
8657
9337
|
type: "button",
|
|
8658
9338
|
style: removeButtonStyle,
|
|
8659
9339
|
onClick: () => handleRemoveTable(table.id),
|
|
8660
|
-
"aria-label": `Remove ${table.name}`,
|
|
9340
|
+
"aria-label": `Remove ${getDisplayName(table.name)}`,
|
|
8661
9341
|
children: /* @__PURE__ */ jsx(Icon, { name: "x", size: 12 })
|
|
8662
9342
|
}
|
|
8663
9343
|
)
|
|
@@ -8691,7 +9371,7 @@ function TableSelector({
|
|
|
8691
9371
|
title: suggestion.relationship,
|
|
8692
9372
|
children: [
|
|
8693
9373
|
/* @__PURE__ */ jsx(Icon, { name: "plus", size: 10 }),
|
|
8694
|
-
suggestion.table
|
|
9374
|
+
getDisplayName(suggestion.table)
|
|
8695
9375
|
]
|
|
8696
9376
|
},
|
|
8697
9377
|
suggestion.table
|
|
@@ -8700,7 +9380,258 @@ function TableSelector({
|
|
|
8700
9380
|
] })
|
|
8701
9381
|
] });
|
|
8702
9382
|
}
|
|
9383
|
+
function parseContent(content) {
|
|
9384
|
+
const startToken = "```sql";
|
|
9385
|
+
const endToken = "```";
|
|
9386
|
+
const parts = [];
|
|
9387
|
+
let cursor = 0;
|
|
9388
|
+
while (cursor < content.length) {
|
|
9389
|
+
const start = content.indexOf(startToken, cursor);
|
|
9390
|
+
if (start === -1) break;
|
|
9391
|
+
if (start > cursor) {
|
|
9392
|
+
parts.push({ type: "text", value: content.slice(cursor, start) });
|
|
9393
|
+
}
|
|
9394
|
+
const sqlStart = content.indexOf("\n", start + startToken.length);
|
|
9395
|
+
if (sqlStart === -1) break;
|
|
9396
|
+
const end = content.indexOf(endToken, sqlStart + 1);
|
|
9397
|
+
if (end === -1) break;
|
|
9398
|
+
const sql = content.slice(sqlStart + 1, end).trim();
|
|
9399
|
+
if (sql) {
|
|
9400
|
+
parts.push({ type: "sql", value: sql });
|
|
9401
|
+
}
|
|
9402
|
+
cursor = end + endToken.length;
|
|
9403
|
+
}
|
|
9404
|
+
if (cursor < content.length) {
|
|
9405
|
+
parts.push({ type: "text", value: content.slice(cursor) });
|
|
9406
|
+
}
|
|
9407
|
+
return parts;
|
|
9408
|
+
}
|
|
9409
|
+
function ChatBubble({ message, onApplySql }) {
|
|
9410
|
+
const { theme } = useTheme();
|
|
9411
|
+
const isUser = message.role === "user";
|
|
9412
|
+
const parts = useMemo(() => parseContent(message.content), [message.content]);
|
|
9413
|
+
const bubbleStyle = {
|
|
9414
|
+
maxWidth: "85%",
|
|
9415
|
+
padding: `${theme.spacing.sm} ${theme.spacing.md}`,
|
|
9416
|
+
borderRadius: theme.radius.md,
|
|
9417
|
+
fontSize: theme.fontSizes.sm,
|
|
9418
|
+
lineHeight: 1.5,
|
|
9419
|
+
whiteSpace: "pre-wrap",
|
|
9420
|
+
wordBreak: "break-word",
|
|
9421
|
+
alignSelf: isUser ? "flex-end" : "flex-start",
|
|
9422
|
+
backgroundColor: isUser ? theme.colors.primary : theme.colors.surface,
|
|
9423
|
+
color: isUser ? "#fff" : theme.colors.text,
|
|
9424
|
+
border: isUser ? "none" : `1px solid ${theme.colors.border}`
|
|
9425
|
+
};
|
|
9426
|
+
const sqlBlockStyle = {
|
|
9427
|
+
backgroundColor: isUser ? "rgba(0,0,0,0.2)" : theme.colors.background,
|
|
9428
|
+
borderRadius: theme.radius.sm,
|
|
9429
|
+
padding: theme.spacing.sm,
|
|
9430
|
+
margin: `${theme.spacing.xs} 0`,
|
|
9431
|
+
fontFamily: theme.fonts.mono,
|
|
9432
|
+
fontSize: theme.fontSizes.xs,
|
|
9433
|
+
overflow: "auto",
|
|
9434
|
+
position: "relative"
|
|
9435
|
+
};
|
|
9436
|
+
const applyBtnContainerStyle = {
|
|
9437
|
+
display: "flex",
|
|
9438
|
+
justifyContent: "flex-end",
|
|
9439
|
+
marginTop: theme.spacing.xs
|
|
9440
|
+
};
|
|
9441
|
+
return /* @__PURE__ */ jsx("div", { style: bubbleStyle, children: parts.map((part, i) => {
|
|
9442
|
+
if (part.type === "sql") {
|
|
9443
|
+
return /* @__PURE__ */ jsxs("div", { "data-testid": `chat-sql-${i}`, children: [
|
|
9444
|
+
/* @__PURE__ */ jsx("pre", { style: sqlBlockStyle, children: /* @__PURE__ */ jsx("code", { children: part.value }) }),
|
|
9445
|
+
onApplySql && /* @__PURE__ */ jsx("div", { style: applyBtnContainerStyle, children: /* @__PURE__ */ jsx(
|
|
9446
|
+
Button,
|
|
9447
|
+
{
|
|
9448
|
+
variant: "ghost",
|
|
9449
|
+
size: "sm",
|
|
9450
|
+
onClick: () => onApplySql(part.value),
|
|
9451
|
+
"data-testid": `apply-sql-btn-${i}`,
|
|
9452
|
+
children: "Apply to Editor"
|
|
9453
|
+
}
|
|
9454
|
+
) })
|
|
9455
|
+
] }, i);
|
|
9456
|
+
}
|
|
9457
|
+
return /* @__PURE__ */ jsx("span", { children: part.value }, i);
|
|
9458
|
+
}) });
|
|
9459
|
+
}
|
|
9460
|
+
function ChatPanel({ currentSql, onApplySql }) {
|
|
9461
|
+
const { theme } = useTheme();
|
|
9462
|
+
const {
|
|
9463
|
+
messages,
|
|
9464
|
+
isStreaming,
|
|
9465
|
+
streamingContent,
|
|
9466
|
+
suggestedSql,
|
|
9467
|
+
sendMessage,
|
|
9468
|
+
clearHistory,
|
|
9469
|
+
error
|
|
9470
|
+
} = useLLMChat();
|
|
9471
|
+
const [input, setInput] = useState("");
|
|
9472
|
+
const messagesEndRef = useRef(null);
|
|
9473
|
+
useEffect(() => {
|
|
9474
|
+
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
9475
|
+
}, [messages, streamingContent]);
|
|
9476
|
+
const handleSend = useCallback(() => {
|
|
9477
|
+
const trimmed = input.trim();
|
|
9478
|
+
if (!trimmed) return;
|
|
9479
|
+
setInput("");
|
|
9480
|
+
void sendMessage(trimmed, currentSql);
|
|
9481
|
+
}, [input, currentSql, sendMessage]);
|
|
9482
|
+
const handleKeyDown = useCallback(
|
|
9483
|
+
(e) => {
|
|
9484
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
9485
|
+
e.preventDefault();
|
|
9486
|
+
handleSend();
|
|
9487
|
+
}
|
|
9488
|
+
},
|
|
9489
|
+
[handleSend]
|
|
9490
|
+
);
|
|
9491
|
+
const containerStyle = {
|
|
9492
|
+
display: "flex",
|
|
9493
|
+
flexDirection: "column",
|
|
9494
|
+
height: "100%",
|
|
9495
|
+
borderLeft: `1px solid ${theme.colors.border}`,
|
|
9496
|
+
backgroundColor: theme.colors.background
|
|
9497
|
+
};
|
|
9498
|
+
const headerStyle = {
|
|
9499
|
+
display: "flex",
|
|
9500
|
+
alignItems: "center",
|
|
9501
|
+
justifyContent: "space-between",
|
|
9502
|
+
padding: `${theme.spacing.sm} ${theme.spacing.md}`,
|
|
9503
|
+
borderBottom: `1px solid ${theme.colors.border}`,
|
|
9504
|
+
flexShrink: 0
|
|
9505
|
+
};
|
|
9506
|
+
const headerTitleStyle = {
|
|
9507
|
+
display: "flex",
|
|
9508
|
+
alignItems: "center",
|
|
9509
|
+
gap: theme.spacing.xs,
|
|
9510
|
+
fontSize: theme.fontSizes.sm,
|
|
9511
|
+
fontWeight: 600,
|
|
9512
|
+
color: theme.colors.text
|
|
9513
|
+
};
|
|
9514
|
+
const messagesStyle = {
|
|
9515
|
+
flex: 1,
|
|
9516
|
+
overflow: "auto",
|
|
9517
|
+
padding: theme.spacing.md,
|
|
9518
|
+
display: "flex",
|
|
9519
|
+
flexDirection: "column",
|
|
9520
|
+
gap: theme.spacing.md
|
|
9521
|
+
};
|
|
9522
|
+
const streamingStyle = {
|
|
9523
|
+
alignSelf: "flex-start",
|
|
9524
|
+
maxWidth: "85%",
|
|
9525
|
+
padding: `${theme.spacing.sm} ${theme.spacing.md}`,
|
|
9526
|
+
borderRadius: theme.radius.md,
|
|
9527
|
+
fontSize: theme.fontSizes.sm,
|
|
9528
|
+
lineHeight: 1.5,
|
|
9529
|
+
whiteSpace: "pre-wrap",
|
|
9530
|
+
wordBreak: "break-word",
|
|
9531
|
+
backgroundColor: theme.colors.surface,
|
|
9532
|
+
color: theme.colors.text,
|
|
9533
|
+
border: `1px solid ${theme.colors.border}`
|
|
9534
|
+
};
|
|
9535
|
+
const suggestedSqlStyle = {
|
|
9536
|
+
padding: theme.spacing.sm,
|
|
9537
|
+
borderTop: `1px solid ${theme.colors.border}`,
|
|
9538
|
+
display: "flex",
|
|
9539
|
+
alignItems: "center",
|
|
9540
|
+
justifyContent: "center",
|
|
9541
|
+
flexShrink: 0
|
|
9542
|
+
};
|
|
9543
|
+
const inputAreaStyle = {
|
|
9544
|
+
display: "flex",
|
|
9545
|
+
gap: theme.spacing.sm,
|
|
9546
|
+
padding: theme.spacing.sm,
|
|
9547
|
+
borderTop: `1px solid ${theme.colors.border}`,
|
|
9548
|
+
flexShrink: 0
|
|
9549
|
+
};
|
|
9550
|
+
const textareaStyle = {
|
|
9551
|
+
flex: 1,
|
|
9552
|
+
resize: "none",
|
|
9553
|
+
border: `1px solid ${theme.colors.border}`,
|
|
9554
|
+
borderRadius: theme.radius.sm,
|
|
9555
|
+
padding: theme.spacing.sm,
|
|
9556
|
+
fontSize: theme.fontSizes.sm,
|
|
9557
|
+
fontFamily: theme.fonts.sans,
|
|
9558
|
+
backgroundColor: theme.colors.surface,
|
|
9559
|
+
color: theme.colors.text,
|
|
9560
|
+
outline: "none",
|
|
9561
|
+
minHeight: "36px",
|
|
9562
|
+
maxHeight: "120px"
|
|
9563
|
+
};
|
|
9564
|
+
const emptyStyle = {
|
|
9565
|
+
flex: 1,
|
|
9566
|
+
display: "flex",
|
|
9567
|
+
alignItems: "center",
|
|
9568
|
+
justifyContent: "center",
|
|
9569
|
+
textAlign: "center",
|
|
9570
|
+
padding: theme.spacing.lg,
|
|
9571
|
+
color: theme.colors.textMuted,
|
|
9572
|
+
fontSize: theme.fontSizes.sm
|
|
9573
|
+
};
|
|
9574
|
+
const errorStyle = {
|
|
9575
|
+
padding: theme.spacing.sm,
|
|
9576
|
+
margin: `0 ${theme.spacing.md}`,
|
|
9577
|
+
borderRadius: theme.radius.sm,
|
|
9578
|
+
backgroundColor: "rgba(239, 68, 68, 0.1)",
|
|
9579
|
+
color: "#ef4444",
|
|
9580
|
+
fontSize: theme.fontSizes.xs,
|
|
9581
|
+
flexShrink: 0
|
|
9582
|
+
};
|
|
9583
|
+
return /* @__PURE__ */ jsxs("div", { style: containerStyle, className: "prismiq-chat-panel", "data-testid": "chat-panel-root", children: [
|
|
9584
|
+
/* @__PURE__ */ jsxs("div", { style: headerStyle, children: [
|
|
9585
|
+
/* @__PURE__ */ jsxs("div", { style: headerTitleStyle, children: [
|
|
9586
|
+
/* @__PURE__ */ jsx(Icon, { name: "edit", size: 16 }),
|
|
9587
|
+
/* @__PURE__ */ jsx("span", { children: "SQL Assistant" })
|
|
9588
|
+
] }),
|
|
9589
|
+
messages.length > 0 && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: clearHistory, "data-testid": "chat-clear", children: "Clear" })
|
|
9590
|
+
] }),
|
|
9591
|
+
messages.length === 0 && !isStreaming ? /* @__PURE__ */ jsxs("div", { style: emptyStyle, "data-testid": "chat-empty", children: [
|
|
9592
|
+
"Ask me to help write SQL queries.",
|
|
9593
|
+
"\n",
|
|
9594
|
+
"I can see your database schema and validate queries."
|
|
9595
|
+
] }) : /* @__PURE__ */ jsxs("div", { style: messagesStyle, "data-testid": "chat-messages", children: [
|
|
9596
|
+
messages.map((msg, i) => /* @__PURE__ */ jsx(ChatBubble, { message: msg, onApplySql }, i)),
|
|
9597
|
+
isStreaming && streamingContent && /* @__PURE__ */ jsxs("div", { style: streamingStyle, "data-testid": "chat-streaming", children: [
|
|
9598
|
+
streamingContent,
|
|
9599
|
+
"\u258D"
|
|
9600
|
+
] }),
|
|
9601
|
+
isStreaming && !streamingContent && /* @__PURE__ */ jsx("div", { style: streamingStyle, "data-testid": "chat-streaming", children: "Thinking..." }),
|
|
9602
|
+
/* @__PURE__ */ jsx("div", { ref: messagesEndRef })
|
|
9603
|
+
] }),
|
|
9604
|
+
error && /* @__PURE__ */ jsx("div", { style: errorStyle, "data-testid": "chat-error", children: error }),
|
|
9605
|
+
suggestedSql && !isStreaming && /* @__PURE__ */ jsx("div", { style: suggestedSqlStyle, children: /* @__PURE__ */ jsx(Button, { variant: "primary", size: "sm", onClick: () => onApplySql(suggestedSql), "data-testid": "chat-apply-sql", children: "Apply SQL to Editor" }) }),
|
|
9606
|
+
/* @__PURE__ */ jsxs("div", { style: inputAreaStyle, children: [
|
|
9607
|
+
/* @__PURE__ */ jsx(
|
|
9608
|
+
"textarea",
|
|
9609
|
+
{
|
|
9610
|
+
style: textareaStyle,
|
|
9611
|
+
value: input,
|
|
9612
|
+
onChange: (e) => setInput(e.target.value),
|
|
9613
|
+
onKeyDown: handleKeyDown,
|
|
9614
|
+
placeholder: "Ask about your data...",
|
|
9615
|
+
rows: 1,
|
|
9616
|
+
disabled: isStreaming,
|
|
9617
|
+
"data-testid": "chat-input"
|
|
9618
|
+
}
|
|
9619
|
+
),
|
|
9620
|
+
/* @__PURE__ */ jsx(
|
|
9621
|
+
Button,
|
|
9622
|
+
{
|
|
9623
|
+
variant: "primary",
|
|
9624
|
+
size: "sm",
|
|
9625
|
+
onClick: handleSend,
|
|
9626
|
+
disabled: isStreaming || !input.trim(),
|
|
9627
|
+
"data-testid": "chat-send",
|
|
9628
|
+
children: /* @__PURE__ */ jsx(Icon, { name: "play", size: 16 })
|
|
9629
|
+
}
|
|
9630
|
+
)
|
|
9631
|
+
] })
|
|
9632
|
+
] });
|
|
9633
|
+
}
|
|
8703
9634
|
|
|
8704
|
-
export { AggregationPicker, AnalyticsProvider, AutoSaveIndicator, Badge, Button, CalculatedFieldBuilder, Checkbox, CollapsibleSection, ColorPaletteSelector, ColumnNode, ColumnSelector, CrossFilterProvider, CustomSQLEditor, Dialog, DialogFooter, DialogHeader, Dropdown, DropdownItem, DropdownSeparator, EmptyDashboard, EmptyState, ErrorBoundary, ErrorFallback, ExpressionEditor, FilterBuilder, FilterRow, FilterValueInput, Icon, Input, JoinBuilder, JoinRow, NoData, NoResults, Pagination, PrismiqClient, PrismiqError, QueryBuilder, QueryBuilderToolbar, QueryPreview, ResultsTable, SavedQueryPicker, SchemaExplorer, Select, SelectedColumn, Skeleton, SkeletonChart, SkeletonMetricCard, SkeletonTable, SkeletonText, SortBuilder, SortRow, TableCell, TableHeader, TableNode, TableRow, TableSelector, TimeSeriesConfig, Tooltip, WidgetErrorBoundary, useAnalytics, useAnalyticsCallbacks, useChartData, useCrossFilterOptional, useCustomSQL, useDashboard, useDashboardMutations, useDashboardPinStatus, useDashboards, useDebouncedLayoutSave, usePinMutations, usePinnedDashboards, useQuery, useSavedQueries, useSchema };
|
|
8705
|
-
//# sourceMappingURL=chunk-
|
|
8706
|
-
//# sourceMappingURL=chunk-
|
|
9635
|
+
export { AggregationPicker, AnalyticsProvider, AutoSaveIndicator, Badge, Button, CalculatedFieldBuilder, ChatBubble, ChatPanel, Checkbox, CollapsibleSection, ColorPaletteSelector, ColumnNode, ColumnSelector, CrossFilterProvider, CustomSQLEditor, Dialog, DialogFooter, DialogHeader, Dropdown, DropdownItem, DropdownSeparator, EmptyDashboard, EmptyState, ErrorBoundary, ErrorFallback, ExpressionEditor, FilterBuilder, FilterRow, FilterValueInput, Icon, Input, JoinBuilder, JoinRow, NoData, NoResults, Pagination, PrismiqClient, PrismiqError, QueryBuilder, QueryBuilderToolbar, QueryPreview, ResultsTable, SavedQueryPicker, SchemaExplorer, Select, SelectedColumn, Skeleton, SkeletonChart, SkeletonMetricCard, SkeletonTable, SkeletonText, SortBuilder, SortRow, TableCell, TableHeader, TableNode, TableRow, TableSelector, TimeSeriesConfig, Tooltip, WidgetErrorBoundary, useAnalytics, useAnalyticsCallbacks, useChartData, useCrossFilterOptional, useCustomSQL, useDashboard, useDashboardMutations, useDashboardPinStatus, useDashboards, useDebouncedLayoutSave, useLLMChat, useLLMStatus, usePinMutations, usePinnedDashboards, useQuery, useSavedQueries, useSchema };
|
|
9636
|
+
//# sourceMappingURL=chunk-GELI7MDZ.js.map
|
|
9637
|
+
//# sourceMappingURL=chunk-GELI7MDZ.js.map
|