2025-04-27 12:37:02 +05:30
|
|
|
/* eslint-disable no-nested-ternary */
|
|
|
|
|
|
|
|
|
|
import './CodeMirrorWhereClause.styles.scss';
|
|
|
|
|
|
2025-04-27 15:34:55 +05:30
|
|
|
import {
|
|
|
|
|
CheckCircleFilled,
|
|
|
|
|
CloseCircleFilled,
|
|
|
|
|
InfoCircleOutlined,
|
|
|
|
|
QuestionCircleOutlined,
|
|
|
|
|
} from '@ant-design/icons';
|
2025-04-27 12:37:02 +05:30
|
|
|
import CodeMirror, { EditorView } from '@uiw/react-codemirror';
|
2025-04-27 15:34:55 +05:30
|
|
|
import { Badge, Card, Divider, Space, Tooltip, Typography } from 'antd';
|
2025-04-27 12:37:02 +05:30
|
|
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
|
|
|
import { IQueryContext, IValidationResult } from 'types/antlrQueryTypes';
|
|
|
|
|
import { getQueryContextAtCursor, validateQuery } from 'utils/antlrQueryUtils';
|
|
|
|
|
|
2025-04-27 15:34:55 +05:30
|
|
|
const { Text, Title } = Typography;
|
2025-04-27 12:37:02 +05:30
|
|
|
|
|
|
|
|
function CodeMirrorWhereClause(): JSX.Element {
|
|
|
|
|
const [query, setQuery] = useState<string>('');
|
|
|
|
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
|
|
|
|
const [queryContext, setQueryContext] = useState<IQueryContext | null>(null);
|
|
|
|
|
const [validation, setValidation] = useState<IValidationResult>({
|
|
|
|
|
isValid: false,
|
|
|
|
|
message: '',
|
|
|
|
|
errors: [],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const [cursorPos, setCursorPos] = useState({ line: 0, ch: 0 });
|
|
|
|
|
const lastPosRef = useRef<{ line: number; ch: number }>({ line: 0, ch: 0 });
|
|
|
|
|
|
|
|
|
|
const handleUpdate = (viewUpdate: { view: EditorView }): void => {
|
|
|
|
|
const selection = viewUpdate.view.state.selection.main;
|
|
|
|
|
const pos = selection.head;
|
|
|
|
|
|
|
|
|
|
const lineInfo = viewUpdate.view.state.doc.lineAt(pos);
|
|
|
|
|
const newPos = {
|
|
|
|
|
line: lineInfo.number,
|
|
|
|
|
ch: pos - lineInfo.from,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const lastPos = lastPosRef.current;
|
|
|
|
|
|
|
|
|
|
// Only update if cursor position actually changed
|
|
|
|
|
if (newPos.line !== lastPos.line || newPos.ch !== lastPos.ch) {
|
|
|
|
|
setCursorPos(newPos);
|
|
|
|
|
lastPosRef.current = newPos;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
console.log({
|
|
|
|
|
cursorPos,
|
|
|
|
|
queryContext,
|
|
|
|
|
validation,
|
|
|
|
|
isLoading,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const handleQueryChange = useCallback(async (newQuery: string) => {
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
setQuery(newQuery);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const validationResponse = validateQuery(newQuery);
|
|
|
|
|
setValidation(validationResponse);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
setValidation({
|
|
|
|
|
isValid: false,
|
|
|
|
|
message: 'Failed to process query',
|
|
|
|
|
errors: [error instanceof Error ? error.message : 'Unknown error'],
|
|
|
|
|
});
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (query) {
|
|
|
|
|
const context = getQueryContextAtCursor(query, cursorPos.ch);
|
|
|
|
|
setQueryContext(context as IQueryContext);
|
|
|
|
|
}
|
|
|
|
|
}, [query, cursorPos]);
|
|
|
|
|
|
|
|
|
|
const handleChange = (value: string): void => {
|
|
|
|
|
setQuery(value);
|
|
|
|
|
handleQueryChange(value);
|
|
|
|
|
};
|
|
|
|
|
|
2025-04-27 15:34:55 +05:30
|
|
|
const renderContextBadge = (): JSX.Element | null => {
|
|
|
|
|
if (!queryContext) return null;
|
|
|
|
|
|
|
|
|
|
let color = 'black';
|
|
|
|
|
let text = 'Unknown';
|
|
|
|
|
|
|
|
|
|
if (queryContext.isInKey) {
|
|
|
|
|
color = 'blue';
|
|
|
|
|
text = 'Key';
|
|
|
|
|
} else if (queryContext.isInOperator) {
|
|
|
|
|
color = 'purple';
|
|
|
|
|
text = 'Operator';
|
|
|
|
|
} else if (queryContext.isInValue) {
|
|
|
|
|
color = 'green';
|
|
|
|
|
text = 'Value';
|
|
|
|
|
} else if (queryContext.isInFunction) {
|
|
|
|
|
color = 'orange';
|
|
|
|
|
text = 'Function';
|
|
|
|
|
} else if (queryContext.isInConjunction) {
|
|
|
|
|
color = 'magenta';
|
|
|
|
|
text = 'Conjunction';
|
|
|
|
|
} else if (queryContext.isInParenthesis) {
|
|
|
|
|
color = 'grey';
|
|
|
|
|
text = 'Parenthesis';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Badge
|
|
|
|
|
color={color}
|
|
|
|
|
text={text}
|
|
|
|
|
style={{
|
|
|
|
|
color: 'black',
|
|
|
|
|
}}
|
2025-04-27 12:37:02 +05:30
|
|
|
/>
|
2025-04-27 15:34:55 +05:30
|
|
|
);
|
|
|
|
|
};
|
2025-04-27 12:37:02 +05:30
|
|
|
|
2025-04-27 15:34:55 +05:30
|
|
|
return (
|
|
|
|
|
<div className="code-mirror-where-clause">
|
|
|
|
|
<Card
|
|
|
|
|
size="small"
|
|
|
|
|
title={<Title level={5}>Where Clause</Title>}
|
|
|
|
|
extra={
|
|
|
|
|
<Tooltip title="Write a query to filter your data">
|
|
|
|
|
<QuestionCircleOutlined />
|
|
|
|
|
</Tooltip>
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<CodeMirror
|
|
|
|
|
value={query}
|
|
|
|
|
theme="dark"
|
|
|
|
|
onChange={handleChange}
|
|
|
|
|
onUpdate={handleUpdate}
|
|
|
|
|
placeholder="Enter your query (e.g., status = 'error' AND service = 'frontend')"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<Space className="cursor-position" size={4}>
|
|
|
|
|
<InfoCircleOutlined />
|
|
|
|
|
<Text style={{ color: 'black' }}>
|
|
|
|
|
Line: {cursorPos.line}, Position: {cursorPos.ch}
|
|
|
|
|
</Text>
|
|
|
|
|
</Space>
|
|
|
|
|
|
|
|
|
|
<Divider style={{ margin: '8px 0' }} />
|
|
|
|
|
|
|
|
|
|
<div className="query-validation">
|
|
|
|
|
<Text>Status:</Text>
|
|
|
|
|
<div className={validation.isValid ? 'valid' : 'invalid'}>
|
|
|
|
|
{validation.isValid ? (
|
|
|
|
|
<>
|
|
|
|
|
<CheckCircleFilled /> Valid
|
|
|
|
|
</>
|
|
|
|
|
) : (
|
|
|
|
|
<>
|
|
|
|
|
<CloseCircleFilled /> Invalid
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
{validation.message && (
|
|
|
|
|
<Tooltip title={validation.message}>
|
|
|
|
|
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
|
|
|
|
</Tooltip>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</Card>
|
2025-04-27 12:37:02 +05:30
|
|
|
|
|
|
|
|
{queryContext && (
|
2025-04-27 15:34:55 +05:30
|
|
|
<Card size="small" title="Current Context" className="query-context">
|
2025-04-27 12:37:02 +05:30
|
|
|
<div className="context-details">
|
2025-04-27 15:34:55 +05:30
|
|
|
<Space direction="vertical" size={4}>
|
|
|
|
|
<Space>
|
|
|
|
|
<Text strong style={{ color: 'black' }}>
|
|
|
|
|
Token:
|
|
|
|
|
</Text>
|
|
|
|
|
<Text code style={{ color: 'black' }}>
|
|
|
|
|
{queryContext.currentToken || '-'}
|
|
|
|
|
</Text>
|
|
|
|
|
</Space>
|
|
|
|
|
<Space>
|
|
|
|
|
<Text strong style={{ color: 'black' }}>
|
|
|
|
|
Type:
|
|
|
|
|
</Text>
|
|
|
|
|
<Text style={{ color: 'black' }}>{queryContext.tokenType || '-'}</Text>
|
|
|
|
|
</Space>
|
|
|
|
|
<Space>
|
|
|
|
|
<Text strong style={{ color: 'black' }}>
|
|
|
|
|
Context:
|
|
|
|
|
</Text>
|
|
|
|
|
{renderContextBadge()}
|
|
|
|
|
</Space>
|
|
|
|
|
</Space>
|
2025-04-27 12:37:02 +05:30
|
|
|
</div>
|
2025-04-27 15:34:55 +05:30
|
|
|
</Card>
|
2025-04-27 12:37:02 +05:30
|
|
|
)}
|
|
|
|
|
|
2025-04-27 15:34:55 +05:30
|
|
|
<Card
|
|
|
|
|
size="small"
|
|
|
|
|
title="Query Examples"
|
|
|
|
|
className="query-examples"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: 'var(--bg-vanilla-100)',
|
|
|
|
|
color: 'black',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<div className="query-examples-list">Query Examples</div>
|
2025-04-27 12:37:02 +05:30
|
|
|
<ul>
|
|
|
|
|
<li>
|
2025-04-27 15:34:55 +05:30
|
|
|
<Text code style={{ color: 'black' }}>
|
|
|
|
|
status = 'error'
|
|
|
|
|
</Text>
|
2025-04-27 12:37:02 +05:30
|
|
|
</li>
|
|
|
|
|
<li>
|
2025-04-27 15:34:55 +05:30
|
|
|
<Text code style={{ color: 'black' }}>
|
2025-04-27 12:37:02 +05:30
|
|
|
service = 'frontend' AND level = 'error'
|
|
|
|
|
</Text>
|
|
|
|
|
</li>
|
|
|
|
|
<li>
|
2025-04-27 15:34:55 +05:30
|
|
|
<Text code style={{ color: 'black' }}>
|
|
|
|
|
message LIKE '%timeout%'
|
|
|
|
|
</Text>
|
2025-04-27 12:37:02 +05:30
|
|
|
</li>
|
|
|
|
|
<li>
|
2025-04-27 15:34:55 +05:30
|
|
|
<Text code style={{ color: 'black' }}>
|
|
|
|
|
duration {'>'} 1000
|
|
|
|
|
</Text>
|
2025-04-27 12:37:02 +05:30
|
|
|
</li>
|
|
|
|
|
<li>
|
2025-04-27 15:34:55 +05:30
|
|
|
<Text code style={{ color: 'black' }}>
|
|
|
|
|
tags IN ['prod', 'frontend']
|
|
|
|
|
</Text>
|
2025-04-27 12:37:02 +05:30
|
|
|
</li>
|
|
|
|
|
<li>
|
2025-04-27 15:34:55 +05:30
|
|
|
<Text code style={{ color: 'black' }}>
|
2025-04-27 12:37:02 +05:30
|
|
|
NOT (status = 'error' OR level = 'error')
|
|
|
|
|
</Text>
|
|
|
|
|
</li>
|
|
|
|
|
</ul>
|
2025-04-27 15:34:55 +05:30
|
|
|
</Card>
|
2025-04-27 12:37:02 +05:30
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default CodeMirrorWhereClause;
|