diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 0031ac2632..f5f5e5610a 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -36,6 +36,7 @@ jobs: run: | echo 'INTERCOM_APP_ID="${{ secrets.INTERCOM_APP_ID }}"' > frontend/.env echo 'SEGMENT_ID="${{ secrets.SEGMENT_ID }}"' >> frontend/.env + echo 'CLARITY_PROJECT_ID="${{ secrets.CLARITY_PROJECT_ID }}"' >> frontend/.env - name: Install dependencies run: cd frontend && yarn install - name: Run ESLint diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index a284ca5b47..98ee1e0fc4 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -133,6 +133,7 @@ jobs: run: | echo 'INTERCOM_APP_ID="${{ secrets.INTERCOM_APP_ID }}"' > frontend/.env echo 'SEGMENT_ID="${{ secrets.SEGMENT_ID }}"' >> frontend/.env + echo 'CLARITY_PROJECT_ID="${{ secrets.CLARITY_PROJECT_ID }}"' >> frontend/.env - name: Install dependencies working-directory: frontend run: yarn install diff --git a/frontend/package.json b/frontend/package.json index 8e013ce78a..da217e7888 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -84,9 +84,11 @@ "react-helmet-async": "1.3.0", "react-i18next": "^11.16.1", "react-intersection-observer": "9.4.1", + "react-markdown": "8.0.7", "react-query": "^3.34.19", "react-redux": "^7.2.2", "react-router-dom": "^5.2.0", + "react-syntax-highlighter": "15.5.0", "react-use": "^17.3.2", "react-virtuoso": "4.0.3", "redux": "^4.0.5", @@ -150,6 +152,7 @@ "@types/react-redux": "^7.1.11", "@types/react-resizable": "3.0.3", "@types/react-router-dom": "^5.1.6", + "@types/react-syntax-highlighter": "15.5.7", "@types/styled-components": "^5.1.4", "@types/uuid": "^8.3.1", "@types/webpack": "^5.28.0", @@ -183,6 +186,7 @@ "lint-staged": "^12.5.0", "portfinder-sync": "^0.0.2", "prettier": "2.2.1", + "raw-loader": "4.0.2", "react-hooks-testing-library": "0.6.0", "react-hot-loader": "^4.13.0", "react-resizable": "3.0.4", diff --git a/frontend/src/components/MarkdownRenderer/CodeCopyBtn/CodeCopyBtn.scss b/frontend/src/components/MarkdownRenderer/CodeCopyBtn/CodeCopyBtn.scss new file mode 100644 index 0000000000..78bfa54dad --- /dev/null +++ b/frontend/src/components/MarkdownRenderer/CodeCopyBtn/CodeCopyBtn.scss @@ -0,0 +1,34 @@ +.code-snippet-container { + position: relative; + background-color: rgb(43, 43, 43); +} + +.code-copy-btn { + position: absolute; + top: 8px; + right: 8px; + display: flex; + justify-content: flex-end; + align-items: center; + + button { + cursor: pointer; + + background-color: rgba($color: #1d1d1d, $alpha: 0.7); + color: white; + border: none; + padding: 8px; + border-radius: 3px; + transition: all 0.1s; + + &:hover { + background-color: rgba($color: #1d1d1d, $alpha: 1); + } + } + + &.copied { + button { + background-color: rgba($color: #52c41a, $alpha: 1); + } + } +} diff --git a/frontend/src/components/MarkdownRenderer/CodeCopyBtn/CodeCopyBtn.tsx b/frontend/src/components/MarkdownRenderer/CodeCopyBtn/CodeCopyBtn.tsx new file mode 100644 index 0000000000..e23882cc71 --- /dev/null +++ b/frontend/src/components/MarkdownRenderer/CodeCopyBtn/CodeCopyBtn.tsx @@ -0,0 +1,32 @@ +import './CodeCopyBtn.scss'; + +import { CheckOutlined, CopyOutlined } from '@ant-design/icons'; +import cx from 'classnames'; +import { useState } from 'react'; + +export default function CodeCopyBtn({ + children, +}: { + children: React.ReactNode; +}): JSX.Element { + const [isSnippetCopied, setIsSnippetCopied] = useState(false); + + const handleClick = (): void => { + if (children && Array.isArray(children)) { + setIsSnippetCopied(true); + navigator.clipboard.writeText(children[0].props.children[0]).finally(() => { + setTimeout(() => { + setIsSnippetCopied(false); + }, 1000); + }); + } + }; + + return ( +
+ +
+ ); +} diff --git a/frontend/src/components/MarkdownRenderer/MarkdownRenderer.tsx b/frontend/src/components/MarkdownRenderer/MarkdownRenderer.tsx new file mode 100644 index 0000000000..82b438dcd0 --- /dev/null +++ b/frontend/src/components/MarkdownRenderer/MarkdownRenderer.tsx @@ -0,0 +1,43 @@ +/* eslint-disable react/jsx-props-no-spreading */ +import { CodeProps } from 'react-markdown/lib/ast-to-react'; +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { a11yDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'; + +import CodeCopyBtn from './CodeCopyBtn/CodeCopyBtn'; + +function Pre({ children }: { children: React.ReactNode }): JSX.Element { + return ( +
+			{children}
+			{children}
+		
+ ); +} + +function Code({ + node, + inline, + className = 'blog-code', + children, + ...props +}: CodeProps): JSX.Element { + const match = /language-(\w+)/.exec(className || ''); + return !inline && match ? ( + + {String(children).replace(/\n$/, '')} + + ) : ( + + {children} + + ); +} + +export { Code, Pre }; diff --git a/frontend/src/constants/queryBuilder.ts b/frontend/src/constants/queryBuilder.ts index c04005afa6..c53873bc5c 100644 --- a/frontend/src/constants/queryBuilder.ts +++ b/frontend/src/constants/queryBuilder.ts @@ -74,7 +74,7 @@ export const mapOfOperators = { traces: tracesAggregateOperatorOptions, }; -export const mapOfFilters: Record = { +export const mapOfQueryFilters: Record = { metrics: [ // eslint-disable-next-line sonarjs/no-duplicate-string { text: 'Aggregation interval', field: 'stepInterval' }, @@ -94,6 +94,24 @@ export const mapOfFilters: Record = { ], }; +const commonFormulaFilters: QueryAdditionalFilter[] = [ + { + text: 'Having', + field: 'having', + }, + { text: 'Order by', field: 'orderBy' }, + { text: 'Limit', field: 'limit' }, +]; + +export const mapOfFormulaToFilters: Record< + DataSource, + QueryAdditionalFilter[] +> = { + metrics: commonFormulaFilters, + logs: commonFormulaFilters, + traces: commonFormulaFilters, +}; + export const REDUCE_TO_VALUES: SelectOption[] = [ { value: 'last', label: 'Latest of values in timeframe' }, { value: 'sum', label: 'Sum of values in timeframe' }, diff --git a/frontend/src/container/ErrorDetails/index.tsx b/frontend/src/container/ErrorDetails/index.tsx index ff27641c64..33a86f9f50 100644 --- a/frontend/src/container/ErrorDetails/index.tsx +++ b/frontend/src/container/ErrorDetails/index.tsx @@ -1,3 +1,5 @@ +import './styles.scss'; + import { Button, Divider, Space, Typography } from 'antd'; import getNextPrevId from 'api/errors/getNextPrevId'; import Editor from 'components/Editor'; @@ -161,7 +163,9 @@ function ErrorDetails(props: ErrorDetailsProps): JSX.Element { {t('stack_trace')} - {}} value={stackTraceValue} readOnly /> +
+ +
diff --git a/frontend/src/container/ErrorDetails/styles.scss b/frontend/src/container/ErrorDetails/styles.scss new file mode 100644 index 0000000000..31194b488f --- /dev/null +++ b/frontend/src/container/ErrorDetails/styles.scss @@ -0,0 +1,3 @@ +.error-container { + height: 50vh; +} diff --git a/frontend/src/container/LiveLogs/constants.ts b/frontend/src/container/LiveLogs/constants.ts index f2c5ca1c97..83df3759ee 100644 --- a/frontend/src/container/LiveLogs/constants.ts +++ b/frontend/src/container/LiveLogs/constants.ts @@ -2,7 +2,7 @@ import { initialQueriesMap, initialQueryBuilderFormValuesMap, } from 'constants/queryBuilder'; -import { FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config'; +import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config'; import { BaseAutocompleteData, DataTypes, @@ -14,7 +14,7 @@ export const defaultLiveQueryDataConfig: Partial = { aggregateOperator: LogsAggregatorOperator.NOOP, disabled: true, pageSize: 10, - orderBy: [{ columnName: 'timestamp', order: FILTERS.DESC }], + orderBy: [{ columnName: 'timestamp', order: ORDERBY_FILTERS.DESC }], }; type GetDefaultCompositeQueryParams = { diff --git a/frontend/src/container/LogsContextList/ShowButton.tsx b/frontend/src/container/LogsContextList/ShowButton.tsx index 26fbadaf30..7240800af7 100644 --- a/frontend/src/container/LogsContextList/ShowButton.tsx +++ b/frontend/src/container/LogsContextList/ShowButton.tsx @@ -1,5 +1,5 @@ import { Button, Typography } from 'antd'; -import { FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config'; +import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config'; import { ShowButtonWrapper } from './styles'; @@ -19,7 +19,7 @@ function ShowButton({ return ( - Showing 10 lines {order === FILTERS.ASC ? 'after' : 'before'} match + Showing 10 lines {order === ORDERBY_FILTERS.ASC ? 'after' : 'before'} match