feat: show errors

This commit is contained in:
Yunus M 2025-06-15 01:08:16 +05:30
parent af7f1def55
commit 2dae184976
3 changed files with 164 additions and 98 deletions

View File

@ -6,6 +6,46 @@
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Helvetica Neue', sans-serif;
.query-where-clause-editor-container {
display: flex;
flex-direction: row;
.query-where-clause-editor {
flex: 1;
min-width: 0;
}
.query-status-container {
width: 32px;
background-color: #121317 !important;
display: flex;
justify-content: center;
align-items: center;
border: 1px solid var(--bg-slate-200);
border-radius: 2px;
border-top-left-radius: 0px !important;
border-bottom-left-radius: 0px !important;
border-left: none !important;
&.hasErrors {
border-color: var(--bg-cherry-500);
}
}
}
.query-where-clause-editor {
&.hasErrors {
.cm-editor {
.cm-content {
border-color: var(--bg-cherry-500);
border-top-right-radius: 0px !important;
border-bottom-right-radius: 0px !important;
}
}
}
}
.cm-editor {
border-radius: 2px;
overflow: hidden;
@ -381,9 +421,23 @@
background-color: rgba(235, 47, 150, 0.1);
}
}
}
.query-text-preview-container {
display: none;
.query-status-popover {
.ant-popover-arrow {
display: none !important;
}
.ant-popover-content {
background: linear-gradient(
139deg,
rgba(18, 19, 23, 0.8) 0%,
rgba(18, 19, 23, 0.9) 98.68%
);
box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2);
backdrop-filter: blur(20px);
margin-top: -6px !important;
}
}

View File

@ -1,6 +1,6 @@
import './QuerySearch.styles.scss';
import { CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons';
import { CheckCircleFilled } from '@ant-design/icons';
import {
autocompletion,
closeCompletion,
@ -10,15 +10,18 @@ import {
startCompletion,
} from '@codemirror/autocomplete';
import { javascript } from '@codemirror/lang-javascript';
import { Color } from '@signozhq/design-tokens';
import { copilot } from '@uiw/codemirror-theme-copilot';
import CodeMirror, {
EditorView,
Extension,
keymap,
} from '@uiw/react-codemirror';
import { Card, Collapse, Space, Tag, Typography } from 'antd';
import { Button, Card, Collapse, Popover, Tag } from 'antd';
import { getValueSuggestions } from 'api/querySuggestions/getValueSuggestion';
import cx from 'classnames';
import { useGetQueryKeySuggestions } from 'hooks/querySuggestions/useGetQueryKeySuggestions';
import { TriangleAlert } from 'lucide-react';
import { useCallback, useEffect, useRef, useState } from 'react';
import {
IDetailedError,
@ -31,7 +34,6 @@ import { getQueryContextAtCursor } from 'utils/queryContextUtils';
import { queryExamples } from './constants';
const { Text } = Typography;
const { Panel } = Collapse;
// Custom extension to stop events
@ -336,7 +338,14 @@ function QuerySearch(): JSX.Element {
const handleQueryChange = useCallback(async (newQuery: string) => {
setQuery(newQuery);
}, []);
const handleChange = (value: string): void => {
setQuery(value);
handleQueryChange(value);
};
const handleQueryValidation = (newQuery: string): void => {
try {
const validationResponse = validateQuery(newQuery);
setValidation(validationResponse);
@ -347,11 +356,14 @@ function QuerySearch(): JSX.Element {
errors: [error as IDetailedError],
});
}
}, []);
};
const handleChange = (value: string): void => {
setQuery(value);
handleQueryChange(value);
const handleBlur = (): void => {
handleQueryValidation(query);
setIsFocused(false);
if (editorRef.current) {
closeCompletion(editorRef.current);
}
};
const handleExampleClick = (exampleQuery: string): void => {
@ -854,11 +866,16 @@ function QuerySearch(): JSX.Element {
</div>
)}
<div className="query-where-clause-editor-container">
<CodeMirror
value={query}
theme={copilot}
onChange={handleChange}
onUpdate={handleUpdate}
className={cx('query-where-clause-editor', {
isValid: validation.isValid === true,
hasErrors: validation.errors.length > 0,
})}
extensions={[
autocompletion({
override: [autoSuggestions],
@ -889,51 +906,54 @@ function QuerySearch(): JSX.Element {
startCompletion(editorRef.current);
}
}}
onBlur={(): void => {
setIsFocused(false);
if (editorRef.current) {
closeCompletion(editorRef.current);
}
}}
onBlur={handleBlur}
/>
{query && (
<div className="query-text-preview-container">
<Space direction="vertical" size={4}>
<Text className="query-text-preview-title">searchExpr</Text>
<Text className="query-text-preview">{query}</Text>
</Space>
{query && validation.isValid === false && !isFocused && (
<div
className={cx('query-status-container', {
hasErrors: validation.errors.length > 0,
})}
>
<Popover
placement="bottomRight"
showArrow={false}
content={
<div className="query-status-content">
<div className="query-status-content-header">
<div className="query-validation">
<div className="query-validation-status">
<Text>Status:</Text>
<div className={validation.isValid ? 'valid' : 'invalid'}>
{validation.isValid ? (
<Space>
<CheckCircleFilled /> Valid
</Space>
) : (
<Space>
<CloseCircleFilled /> Invalid
</Space>
)}
</div>
</div>
<div className="query-validation-errors">
{validation.errors.map((error) => (
<div key={error.message} className="query-validation-error">
<div className="query-validation-error-line">
{error.line}:{error.column}
<div className="query-validation-error">
{error.line}:{error.column} - {error.message}
</div>
<div className="query-validation-error-message">{error.message}</div>
</div>
))}
</div>
</div>
</div>
</div>
}
overlayClassName="query-status-popover"
>
{validation.isValid ? (
<Button
type="text"
icon={<CheckCircleFilled />}
className="periscope-btn ghost"
/>
) : (
<Button
type="text"
icon={<TriangleAlert size={14} color={Color.BG_CHERRY_500} />}
className="periscope-btn ghost"
/>
)}
</Popover>
</div>
)}
</div>
{showExamples && (
<Card size="small" className="query-examples-card">
@ -976,7 +996,7 @@ function QuerySearch(): JSX.Element {
</Card>
)}
{queryContext && (
{/* {queryContext && (
<Card size="small" title="Current Context" className="query-context">
<div className="context-details">
<Space direction="vertical" size={4}>
@ -1016,7 +1036,7 @@ function QuerySearch(): JSX.Element {
</Space>
</div>
</Card>
)}
)} */}
</div>
);
}

View File

@ -119,17 +119,9 @@ export const validateQuery = (query: string): IValidationResult => {
// Empty query is considered invalid
if (!query.trim()) {
return {
isValid: false,
message: 'Query cannot be empty',
errors: [
{
message: 'Query cannot be empty',
line: 0,
column: 0,
offendingSymbol: '',
expectedTokens: [],
},
],
isValid: true,
message: 'Query is empty',
errors: [],
};
}