Skip to content
This repository was archived by the owner on May 13, 2025. It is now read-only.
18 changes: 12 additions & 6 deletions src/pages/Stream/components/Querier/FilterQueryBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,16 @@ const CombinatorToggle = (props: CombinatorToggleType) => {
const { onCombinatorChange, isOrSelected } = props;
return (
<Box className={classes.toggleBtnContainer}>
<Text style={{fontSize: '0.6rem'}} className={isOrSelected ? activeBtnClass : inActiveBtnClass} onClick={() => onCombinatorChange('or')}>
<Text
style={{ fontSize: '0.6rem' }}
className={isOrSelected ? activeBtnClass : inActiveBtnClass}
onClick={() => onCombinatorChange('or')}>
OR
</Text>
<Text style={{fontSize: '0.6rem'}} className={!isOrSelected ? activeBtnClass : inActiveBtnClass} onClick={() => onCombinatorChange('and')}>
<Text
style={{ fontSize: '0.6rem' }}
className={!isOrSelected ? activeBtnClass : inActiveBtnClass}
onClick={() => onCombinatorChange('and')}>
AND
</Text>
</Box>
Expand Down Expand Up @@ -228,7 +234,7 @@ export const QueryPills = () => {
const { combinator, rules: ruleSets } = appliedQuery;
return (
<ScrollArea scrollbarSize={6} scrollHideDelay={0} offsetScrollbars={false}>
<Stack style={{ height: '100%'}}>
<Stack style={{ height: '100%' }}>
<Stack style={{ flexDirection: 'row' }} gap={8}>
{ruleSets.map((ruleSet, index) => {
const shouldShowCombinatorPill = ruleSets.length !== 1 && index + 1 !== ruleSets.length;
Expand Down Expand Up @@ -258,9 +264,9 @@ export const FilterQueryBuilder = (props: { onClear: () => void; onApply: () =>
}, [query.rules, fields]);

return (
<Stack style={{ height: 500 }}>
<ScrollArea style={{ height: 480 }}>
<Stack gap={0}>
<Stack style={{ height: '100%', justifyContent:'space-between'}}>
<ScrollArea>
<Stack gap={0} pl={20} pr={20}>
{query.rules.map((ruleSet) => {
return <RuleSet ruleSet={ruleSet} key={ruleSet.id} />;
})}
Expand Down
187 changes: 105 additions & 82 deletions src/pages/Stream/components/Querier/QueryCodeEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC, MutableRefObject, useCallback, useEffect } from 'react';
import React, { FC, MutableRefObject, useCallback, useEffect, useState, useRef } from 'react';
import Editor from '@monaco-editor/react';
import { Box, Button, Flex, ScrollArea, Stack, Text, TextInput } from '@mantine/core';
import { ErrorMarker, errChecker } from '../ErrorMarker';
Expand Down Expand Up @@ -32,16 +32,22 @@ const genColumnConfig = (fields: Field[]) => {

const defaultCustSQLQuery = (streamName: string | null) => {
if (streamName && streamName.length > 0) {
return `SELECT * FROM ${streamName} LIMIT ${LOAD_LIMIT};`
return `SELECT * FROM ${streamName} LIMIT ${LOAD_LIMIT};`;
} else {
return ''
return '';
}
}
};

const QueryCodeEditor: FC<{ queryCodeEditorRef: MutableRefObject<any>, onSqlSearchApply: (query: string) => void; onClear: () => void; }> = (props) => {
const QueryCodeEditor: FC<{
queryCodeEditorRef: MutableRefObject<any>;
onSqlSearchApply: (query: string) => void;
onClear: () => void;
}> = (props) => {
const [llmActive] = useAppStore((store) => store.instanceConfig?.llmActive);
const [] = useFilterStore((store) => store);
const [{ isQuerySearchActive, activeMode, savedFilterId, custSearchQuery }] = useLogsStore((store) => store.custQuerySearchState);
const [{ isQuerySearchActive, activeMode, savedFilterId, custSearchQuery }] = useLogsStore(
(store) => store.custQuerySearchState,
);
const [schema] = useStreamStore((store) => store.schema);
const fields = schema?.fields || [];
const editorRef = React.useRef<any>();
Expand All @@ -68,9 +74,9 @@ const QueryCodeEditor: FC<{ queryCodeEditorRef: MutableRefObject<any>, onSqlSear

useEffect(() => {
if (savedFilterId && isSqlSearchActive) {
updateQuery(custSearchQuery)
updateQuery(custSearchQuery);
}
}, [savedFilterId])
}, [savedFilterId]);

const handleAIGenerate = useCallback(() => {
if (!aiQuery?.length) {
Expand Down Expand Up @@ -113,56 +119,54 @@ const QueryCodeEditor: FC<{ queryCodeEditorRef: MutableRefObject<any>, onSqlSear
const sanitizedQuery = sanitiseSqlString(query);
const parsedQuery = sanitizedQuery.replace(/(\r\n|\n|\r)/gm, '');
updateQuery(sanitizedQuery);
props.onSqlSearchApply(parsedQuery)
props.onSqlSearchApply(parsedQuery);
}, [query]);

return (
<Stack style={{ flex: 1 }}>
<ScrollArea>
<Box style={{ marginBottom: 8 }}>
{localLlmActive ? (
<Stack gap={0} style={{ flexDirection: 'row', width: '100%' }}>
<TextInput
type="text"
name="ai_query"
id="ai_query"
value={aiQuery}
onChange={(e) => setAiQuery(e.target.value)}
placeholder="Enter plain text to generate SQL query using OpenAI"
w="85%"
/>
<Button variant="filled" w="15%" color="brandPrimary.4" radius={0} onClick={handleAIGenerate}>
✨ Generate
</Button>
</Stack>
) : (
<Box style={{ width: '100%' }}>
<Box component="a" href="https://www.parseable.com/docs/integrations/llm" target="_blank">
Know More: How to enable SQL generation with OpenAI ?
</Box>
<Stack style={{ flex: 1, justifyContent: 'space-between' }}>
<Box style={{ marginBottom: 8 }}>
{localLlmActive ? (
<Stack gap={0} style={{ flexDirection: 'row', width: '100%' }}>
<TextInput
type="text"
name="ai_query"
id="ai_query"
value={aiQuery}
onChange={(e) => setAiQuery(e.target.value)}
placeholder="Enter plain text to generate SQL query using OpenAI"
w="85%"
/>
<Button variant="filled" w="15%" color="brandPrimary.4" radius={0} onClick={handleAIGenerate}>
✨ Generate
</Button>
</Stack>
) : (
<Box style={{ width: '100%' }}>
<Box component="a" href="https://www.parseable.com/docs/integrations/llm" target="_blank">
Know More: How to enable SQL generation with OpenAI ?
</Box>
)}
</Box>
<SchemaList {...{ currentStream, fields }} />
<Stack style={{ height: 200, flex: 1 }}>
<Editor
defaultLanguage="sql"
value={query}
onChange={handleEditorChange}
options={{
scrollBeyondLastLine: false,
readOnly: false,
fontSize: 10,
wordWrap: 'on',
minimap: { enabled: false },
automaticLayout: true,
mouseWheelZoom: true,
padding: { top: 8 },
}}
// onMount={handleEditorDidMount}
/>
</Stack>
</ScrollArea>
</Box>
)}
</Box>
<SchemaList {...{ currentStream, fields }} />
<Stack style={{ height: 200, flex: 1 }}>
<Editor
defaultLanguage="sql"
value={query}
onChange={handleEditorChange}
options={{
scrollBeyondLastLine: false,
readOnly: false,
fontSize: 10,
wordWrap: 'on',
minimap: { enabled: false },
automaticLayout: true,
mouseWheelZoom: true,
padding: { top: 8 },
}}
// onMount={handleEditorDidMount}
/>
</Stack>
<Stack className={queryCodeStyles.footer} style={{ alignItems: 'center' }}>
<Button onClick={props.onClear} disabled={!isSqlSearchActive} variant="outline">
Clear
Expand All @@ -174,40 +178,59 @@ const QueryCodeEditor: FC<{ queryCodeEditorRef: MutableRefObject<any>, onSqlSear
};

const SchemaList = (props: { currentStream: string | null; fields: Field[] }) => {
const schemaListDivRef: MutableRefObject<HTMLDivElement | null> = useRef(null);
const [schemaDivHeight, setSchemaDivHeight] = useState<number>(0);
const [scrollPosition, onScrollPositionChange] = useState({ x: 0, y: 0 });

useEffect(() => {
const height = schemaListDivRef.current && schemaListDivRef.current.offsetHeight;
setSchemaDivHeight(height ? height : 0);
}, []);
const { currentStream, fields } = props;
if (!fields || fields.length === 0) return null;

const { leftColumns, rightColumns } = genColumnConfig(fields);
const showShadow = schemaDivHeight > 190 ? (!((schemaDivHeight -190) === scrollPosition.y) ? queryCodeStyles.schemaListShadow : ''): '';

return (
<Box>
<Text
style={{
fontSize: '0.7rem',
color: '#098658',
fontFamily: 'monospace',
}}>{`/* Schema for ${currentStream}`}</Text>
<Flex style={{ alignItems: 'flex-start', padding: 6, paddingTop: 4 }}>
<Box style={{ width: '50%' }}>
{leftColumns.map((config, index) => {
return (
<Text
key={index}
style={{ fontSize: '0.7rem', color: '#098658', fontFamily: 'monospace' }}>{`${config}\n\n`}</Text>
);
})}
</Box>
<Box style={{ width: '50%' }}>
{rightColumns.map((config, index) => {
return (
<Text
key={index}
style={{ fontSize: '0.7rem', color: '#098658', fontFamily: 'monospace' }}>{`${config}\n\n`}</Text>
);
})}
<Stack
className={showShadow}
style={{ height: 190 }}>
<ScrollArea scrollbars="y" onScrollPositionChange={onScrollPositionChange}>
<Box ref={schemaListDivRef}>
<Text
style={{
fontSize: '0.7rem',
color: '#098658',
fontFamily: 'monospace',
}}>{`/* Schema for ${currentStream}`}</Text>
<Flex style={{ alignItems: 'flex-start', padding: 6, paddingTop: 4 }}>
<Box style={{ width: '50%' }}>
{leftColumns.map((config, index) => {
return (
<Text
key={index}
style={{ fontSize: '0.7rem', color: '#098658', fontFamily: 'monospace' }}>{`${config}\n\n`}</Text>
);
})}
</Box>
<Box style={{ width: '50%' }}>
{rightColumns.map((config, index) => {
return (
<Text
key={index}
style={{ fontSize: '0.7rem', color: '#098658', fontFamily: 'monospace' }}>{`${config}\n\n`}</Text>
);
})}
</Box>
</Flex>
<Text style={{ fontSize: '0.7rem', color: '#098658', fontFamily: 'monospace' }}> */</Text>
</Box>
</Flex>
<Text style={{ fontSize: '0.7rem', color: '#098658', fontFamily: 'monospace' }}> */</Text>
</Box>
</ScrollArea>
{schemaDivHeight > 190 && scrollPosition.y < 10 ? (
<Text style={{ fontSize: '0.8rem', color: '#095286', textAlign: 'center', paddingBottom:'0.2rem' }}>Scroll to see more</Text>
) : null}
</Stack>
);
};

Expand Down
44 changes: 36 additions & 8 deletions src/pages/Stream/components/Querier/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Group, Menu, Modal, Stack, px, Tooltip } from '@mantine/core';
import { Group, Menu, Modal, Stack, px, Tooltip, Tabs } from '@mantine/core';
import { IconChevronDown, IconCodeCircle, IconFilter, IconFilterEdit, IconFilterPlus } from '@tabler/icons-react';
import classes from '../../styles/Querier.module.css';
import { Text } from '@mantine/core';
Expand Down Expand Up @@ -42,7 +42,29 @@ const SQLEditorPlaceholder = () => {
};

const ModalTitle = ({ title }: { title: string }) => {
return <Text style={{ fontSize: '1rem', fontWeight: 500, marginLeft: '0.5rem' }}>{title}</Text>;
const [, setLogsStore] = useLogsStore((store) => store.custQuerySearchState);
const onChangeCustQueryViewMode = useCallback((mode: 'sql' | 'filters') => {
setLogsStore((store) => toggleCustQuerySearchViewMode(store, mode));
}, []);

return (
<Tabs defaultValue={title} style={{ padding: '0 0.4rem', outline: 'none' }}>
<Tabs.List>
<Tabs.Tab
className={title !== 'Filters' ? classes.tab : ''}
value="Filters"
onClick={() => onChangeCustQueryViewMode('filters')}>
<Text style={{ fontSize: '1rem', fontWeight: 600 }}>Filters</Text>
</Tabs.Tab>
<Tabs.Tab
className={title !== 'SQL' ? classes.tab : ''}
value="SQL"
onClick={() => onChangeCustQueryViewMode('sql')}>
<Text style={{ fontSize: '1rem', fontWeight: 600 }}>SQL</Text>
</Tabs.Tab>
</Tabs.List>
</Tabs>
);
};

const QuerierModal = (props: {
Expand All @@ -62,9 +84,12 @@ const QuerierModal = (props: {
onClose={onClose}
size="auto"
centered
styles={{ body: { padding: '0 0.5rem' }, header: { padding: '1rem', paddingBottom: '0' } }}
styles={{
body: { padding: '0 0.8rem', height: '70vh', width: '50vw', justifyContent: 'center' },
header: { padding: '1rem', paddingBottom: '0' },
}}
title={<ModalTitle title={getLabel(viewMode)} />}>
<Stack style={{ width: '40rem', padding: '1rem', height: '100%' }} gap={0}>
<Stack style={{ padding: '1rem 0.5rem', height: '100%' }} gap={2}>
{viewMode === 'filters' ? (
<FilterQueryBuilder onClear={props.onClear} onApply={props.onFiltersApply} />
) : (
Expand Down Expand Up @@ -100,10 +125,13 @@ const Querier = () => {
return setFilterStore(resetFilters);
}, []);

const triggerRefetch = useCallback((query: string, mode: 'filters' | 'sql', id?: string) => {
const time_filter = id ? _.find(activeSavedFilters, filter => filter.filter_id === id)?.time_filter : null
setLogsStore((store) => applyCustomQuery(store, query, mode, id, time_filter));
}, [activeSavedFilters]);
const triggerRefetch = useCallback(
(query: string, mode: 'filters' | 'sql', id?: string) => {
const time_filter = id ? _.find(activeSavedFilters, (filter) => filter.filter_id === id)?.time_filter : null;
setLogsStore((store) => applyCustomQuery(store, query, mode, id, time_filter));
},
[activeSavedFilters],
);

const onFiltersApply = useCallback(
(opts?: { isUncontrolled?: boolean }) => {
Expand Down
Loading