From 2dae18497628a75b74d7bb6e42f4019a2efac3c4 Mon Sep 17 00:00:00 2001 From: Yunus M Date: Sun, 15 Jun 2025 01:08:16 +0530 Subject: [PATCH] feat: show errors --- .../QuerySearch/QuerySearch.styles.scss | 58 +++++- .../QueryV2/QuerySearch/QuerySearch.tsx | 190 ++++++++++-------- frontend/src/utils/antlrQueryUtils.ts | 14 +- 3 files changed, 164 insertions(+), 98 deletions(-) diff --git a/frontend/src/components/QueryBuilderV2/QueryV2/QuerySearch/QuerySearch.styles.scss b/frontend/src/components/QueryBuilderV2/QueryV2/QuerySearch/QuerySearch.styles.scss index d6e2751e778c..9d95d3ae888e 100644 --- a/frontend/src/components/QueryBuilderV2/QueryV2/QuerySearch/QuerySearch.styles.scss +++ b/frontend/src/components/QueryBuilderV2/QueryV2/QuerySearch/QuerySearch.styles.scss @@ -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; } } diff --git a/frontend/src/components/QueryBuilderV2/QueryV2/QuerySearch/QuerySearch.tsx b/frontend/src/components/QueryBuilderV2/QueryV2/QuerySearch/QuerySearch.tsx index 4ae61500081e..ad7ff5847bdb 100644 --- a/frontend/src/components/QueryBuilderV2/QueryV2/QuerySearch/QuerySearch.tsx +++ b/frontend/src/components/QueryBuilderV2/QueryV2/QuerySearch/QuerySearch.tsx @@ -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,86 +866,94 @@ function QuerySearch(): JSX.Element { )} - { - setIsFocused(true); - if (editorRef.current) { - startCompletion(editorRef.current); - } - }} - onBlur={(): void => { - setIsFocused(false); - if (editorRef.current) { - closeCompletion(editorRef.current); - } - }} - /> +
+ 0, + })} + extensions={[ + autocompletion({ + override: [autoSuggestions], + defaultKeymap: true, + closeOnBlur: true, + activateOnTyping: true, + maxRenderedOptions: 50, + }), + javascript({ jsx: false, typescript: false }), + EditorView.lineWrapping, + stopEventsExtension, + disallowMultipleSpaces, + keymap.of([ + ...completionKeymap, + { + key: 'Escape', + run: closeCompletion, + }, + ]), + ]} + placeholder="Enter your query (e.g., status = 'error' AND service = 'frontend')" + basicSetup={{ + lineNumbers: false, + }} + onFocus={(): void => { + setIsFocused(true); + if (editorRef.current) { + startCompletion(editorRef.current); + } + }} + onBlur={handleBlur} + /> - {query && ( -
- - searchExpr - {query} - - -
-
- Status: -
- {validation.isValid ? ( - - Valid - - ) : ( - - Invalid - - )} -
-
- -
- {validation.errors.map((error) => ( -
-
- {error.line}:{error.column} + {query && validation.isValid === false && !isFocused && ( +
0, + })} + > + +
+
+
+ {validation.errors.map((error) => ( +
+
+ {error.line}:{error.column} - {error.message} +
+
+ ))} +
+
- -
{error.message}
- ))} -
+ } + overlayClassName="query-status-popover" + > + {validation.isValid ? ( +
-
- )} + )} +
{showExamples && ( @@ -976,7 +996,7 @@ function QuerySearch(): JSX.Element { )} - {queryContext && ( + {/* {queryContext && (
@@ -1016,7 +1036,7 @@ function QuerySearch(): JSX.Element {
- )} + )} */}
); } diff --git a/frontend/src/utils/antlrQueryUtils.ts b/frontend/src/utils/antlrQueryUtils.ts index 0b450bb363ff..7dc0ba972662 100644 --- a/frontend/src/utils/antlrQueryUtils.ts +++ b/frontend/src/utils/antlrQueryUtils.ts @@ -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: [], }; }