mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-17 15:36:48 +00:00
feat: show errors
This commit is contained in:
parent
af7f1def55
commit
2dae184976
@ -6,6 +6,46 @@
|
|||||||
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||||
'Helvetica Neue', sans-serif;
|
'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 {
|
.cm-editor {
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -381,9 +421,23 @@
|
|||||||
background-color: rgba(235, 47, 150, 0.1);
|
background-color: rgba(235, 47, 150, 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.query-text-preview-container {
|
.query-status-popover {
|
||||||
display: none;
|
.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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import './QuerySearch.styles.scss';
|
import './QuerySearch.styles.scss';
|
||||||
|
|
||||||
import { CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons';
|
import { CheckCircleFilled } from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
autocompletion,
|
autocompletion,
|
||||||
closeCompletion,
|
closeCompletion,
|
||||||
@ -10,15 +10,18 @@ import {
|
|||||||
startCompletion,
|
startCompletion,
|
||||||
} from '@codemirror/autocomplete';
|
} from '@codemirror/autocomplete';
|
||||||
import { javascript } from '@codemirror/lang-javascript';
|
import { javascript } from '@codemirror/lang-javascript';
|
||||||
|
import { Color } from '@signozhq/design-tokens';
|
||||||
import { copilot } from '@uiw/codemirror-theme-copilot';
|
import { copilot } from '@uiw/codemirror-theme-copilot';
|
||||||
import CodeMirror, {
|
import CodeMirror, {
|
||||||
EditorView,
|
EditorView,
|
||||||
Extension,
|
Extension,
|
||||||
keymap,
|
keymap,
|
||||||
} from '@uiw/react-codemirror';
|
} 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 { getValueSuggestions } from 'api/querySuggestions/getValueSuggestion';
|
||||||
|
import cx from 'classnames';
|
||||||
import { useGetQueryKeySuggestions } from 'hooks/querySuggestions/useGetQueryKeySuggestions';
|
import { useGetQueryKeySuggestions } from 'hooks/querySuggestions/useGetQueryKeySuggestions';
|
||||||
|
import { TriangleAlert } from 'lucide-react';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
IDetailedError,
|
IDetailedError,
|
||||||
@ -31,7 +34,6 @@ import { getQueryContextAtCursor } from 'utils/queryContextUtils';
|
|||||||
|
|
||||||
import { queryExamples } from './constants';
|
import { queryExamples } from './constants';
|
||||||
|
|
||||||
const { Text } = Typography;
|
|
||||||
const { Panel } = Collapse;
|
const { Panel } = Collapse;
|
||||||
|
|
||||||
// Custom extension to stop events
|
// Custom extension to stop events
|
||||||
@ -336,7 +338,14 @@ function QuerySearch(): JSX.Element {
|
|||||||
|
|
||||||
const handleQueryChange = useCallback(async (newQuery: string) => {
|
const handleQueryChange = useCallback(async (newQuery: string) => {
|
||||||
setQuery(newQuery);
|
setQuery(newQuery);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleChange = (value: string): void => {
|
||||||
|
setQuery(value);
|
||||||
|
handleQueryChange(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleQueryValidation = (newQuery: string): void => {
|
||||||
try {
|
try {
|
||||||
const validationResponse = validateQuery(newQuery);
|
const validationResponse = validateQuery(newQuery);
|
||||||
setValidation(validationResponse);
|
setValidation(validationResponse);
|
||||||
@ -347,11 +356,14 @@ function QuerySearch(): JSX.Element {
|
|||||||
errors: [error as IDetailedError],
|
errors: [error as IDetailedError],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, []);
|
};
|
||||||
|
|
||||||
const handleChange = (value: string): void => {
|
const handleBlur = (): void => {
|
||||||
setQuery(value);
|
handleQueryValidation(query);
|
||||||
handleQueryChange(value);
|
setIsFocused(false);
|
||||||
|
if (editorRef.current) {
|
||||||
|
closeCompletion(editorRef.current);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleExampleClick = (exampleQuery: string): void => {
|
const handleExampleClick = (exampleQuery: string): void => {
|
||||||
@ -854,86 +866,94 @@ function QuerySearch(): JSX.Element {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<CodeMirror
|
<div className="query-where-clause-editor-container">
|
||||||
value={query}
|
<CodeMirror
|
||||||
theme={copilot}
|
value={query}
|
||||||
onChange={handleChange}
|
theme={copilot}
|
||||||
onUpdate={handleUpdate}
|
onChange={handleChange}
|
||||||
extensions={[
|
onUpdate={handleUpdate}
|
||||||
autocompletion({
|
className={cx('query-where-clause-editor', {
|
||||||
override: [autoSuggestions],
|
isValid: validation.isValid === true,
|
||||||
defaultKeymap: true,
|
hasErrors: validation.errors.length > 0,
|
||||||
closeOnBlur: true,
|
})}
|
||||||
activateOnTyping: true,
|
extensions={[
|
||||||
maxRenderedOptions: 50,
|
autocompletion({
|
||||||
}),
|
override: [autoSuggestions],
|
||||||
javascript({ jsx: false, typescript: false }),
|
defaultKeymap: true,
|
||||||
EditorView.lineWrapping,
|
closeOnBlur: true,
|
||||||
stopEventsExtension,
|
activateOnTyping: true,
|
||||||
disallowMultipleSpaces,
|
maxRenderedOptions: 50,
|
||||||
keymap.of([
|
}),
|
||||||
...completionKeymap,
|
javascript({ jsx: false, typescript: false }),
|
||||||
{
|
EditorView.lineWrapping,
|
||||||
key: 'Escape',
|
stopEventsExtension,
|
||||||
run: closeCompletion,
|
disallowMultipleSpaces,
|
||||||
},
|
keymap.of([
|
||||||
]),
|
...completionKeymap,
|
||||||
]}
|
{
|
||||||
placeholder="Enter your query (e.g., status = 'error' AND service = 'frontend')"
|
key: 'Escape',
|
||||||
basicSetup={{
|
run: closeCompletion,
|
||||||
lineNumbers: false,
|
},
|
||||||
}}
|
]),
|
||||||
onFocus={(): void => {
|
]}
|
||||||
setIsFocused(true);
|
placeholder="Enter your query (e.g., status = 'error' AND service = 'frontend')"
|
||||||
if (editorRef.current) {
|
basicSetup={{
|
||||||
startCompletion(editorRef.current);
|
lineNumbers: false,
|
||||||
}
|
}}
|
||||||
}}
|
onFocus={(): void => {
|
||||||
onBlur={(): void => {
|
setIsFocused(true);
|
||||||
setIsFocused(false);
|
if (editorRef.current) {
|
||||||
if (editorRef.current) {
|
startCompletion(editorRef.current);
|
||||||
closeCompletion(editorRef.current);
|
}
|
||||||
}
|
}}
|
||||||
}}
|
onBlur={handleBlur}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{query && (
|
{query && validation.isValid === false && !isFocused && (
|
||||||
<div className="query-text-preview-container">
|
<div
|
||||||
<Space direction="vertical" size={4}>
|
className={cx('query-status-container', {
|
||||||
<Text className="query-text-preview-title">searchExpr</Text>
|
hasErrors: validation.errors.length > 0,
|
||||||
<Text className="query-text-preview">{query}</Text>
|
})}
|
||||||
</Space>
|
>
|
||||||
|
<Popover
|
||||||
<div className="query-validation">
|
placement="bottomRight"
|
||||||
<div className="query-validation-status">
|
showArrow={false}
|
||||||
<Text>Status:</Text>
|
content={
|
||||||
<div className={validation.isValid ? 'valid' : 'invalid'}>
|
<div className="query-status-content">
|
||||||
{validation.isValid ? (
|
<div className="query-status-content-header">
|
||||||
<Space>
|
<div className="query-validation">
|
||||||
<CheckCircleFilled /> Valid
|
<div className="query-validation-errors">
|
||||||
</Space>
|
{validation.errors.map((error) => (
|
||||||
) : (
|
<div key={error.message} className="query-validation-error">
|
||||||
<Space>
|
<div className="query-validation-error">
|
||||||
<CloseCircleFilled /> Invalid
|
{error.line}:{error.column} - {error.message}
|
||||||
</Space>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</div>
|
))}
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
<div className="query-validation-error-message">{error.message}</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>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
|
|
||||||
{showExamples && (
|
{showExamples && (
|
||||||
<Card size="small" className="query-examples-card">
|
<Card size="small" className="query-examples-card">
|
||||||
@ -976,7 +996,7 @@ function QuerySearch(): JSX.Element {
|
|||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{queryContext && (
|
{/* {queryContext && (
|
||||||
<Card size="small" title="Current Context" className="query-context">
|
<Card size="small" title="Current Context" className="query-context">
|
||||||
<div className="context-details">
|
<div className="context-details">
|
||||||
<Space direction="vertical" size={4}>
|
<Space direction="vertical" size={4}>
|
||||||
@ -1016,7 +1036,7 @@ function QuerySearch(): JSX.Element {
|
|||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)} */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -119,17 +119,9 @@ export const validateQuery = (query: string): IValidationResult => {
|
|||||||
// Empty query is considered invalid
|
// Empty query is considered invalid
|
||||||
if (!query.trim()) {
|
if (!query.trim()) {
|
||||||
return {
|
return {
|
||||||
isValid: false,
|
isValid: true,
|
||||||
message: 'Query cannot be empty',
|
message: 'Query is empty',
|
||||||
errors: [
|
errors: [],
|
||||||
{
|
|
||||||
message: 'Query cannot be empty',
|
|
||||||
line: 0,
|
|
||||||
column: 0,
|
|
||||||
offendingSymbol: '',
|
|
||||||
expectedTokens: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user