diff --git a/redisinsight/ui/src/components/query/QueryWrapper.tsx b/redisinsight/ui/src/components/query/QueryWrapper.tsx
index 5bf95ce62d..f4888865e8 100644
--- a/redisinsight/ui/src/components/query/QueryWrapper.tsx
+++ b/redisinsight/ui/src/components/query/QueryWrapper.tsx
@@ -1,9 +1,13 @@
import React from 'react'
import { useSelector } from 'react-redux'
import { EuiLoadingContent } from '@elastic/eui'
+import { decode } from 'html-entities'
+import { useParams } from 'react-router-dom'
+
+import { sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
import { appRedisCommandsSelector } from 'uiSrc/slices/app/redis-commands'
+import { getMultiCommands, removeMonacoComments, splitMonacoValuePerLines } from 'uiSrc/utils'
import Query from './Query'
-
import styles from './Query/styles.module.scss'
export interface Props {
@@ -12,11 +16,46 @@ export interface Props {
setQuery: (script: string) => void;
setQueryEl: Function;
onKeyDown?: (e: React.KeyboardEvent, script: string) => void;
- onSubmit: () => void;
+ onSubmit: (value?: string) => void;
}
const QueryWrapper = (props: Props) => {
const { query = '', loading, setQuery, setQueryEl, onKeyDown, onSubmit } = props
- const { loading: isCommandsLoading } = useSelector(appRedisCommandsSelector)
+ const { instanceId = '' } = useParams<{ instanceId: string }>()
+ const {
+ loading: isCommandsLoading,
+ commandsArray: REDIS_COMMANDS_ARRAY,
+ } = useSelector(appRedisCommandsSelector)
+
+ const sendEventSubmitTelemetry = (commandInit = query) => {
+ const eventData = (() => {
+ const commands = splitMonacoValuePerLines(commandInit)
+
+ const [commandLine, ...rest] = commands.map((command = '') => {
+ const matchedCommand = REDIS_COMMANDS_ARRAY.find((commandName) =>
+ command.toUpperCase().startsWith(commandName))
+ return matchedCommand ?? command.split(' ')?.[0]
+ })
+ const multiCommands = getMultiCommands(rest)
+
+ const command = removeMonacoComments(decode([commandLine, multiCommands].join('\n')).trim())
+
+ return {
+ command,
+ databaseId: instanceId,
+ multiple: multiCommands ? 'Multiple' : 'Single'
+ }
+ })()
+
+ sendEventTelemetry({
+ event: TelemetryEvent.WORKBENCH_COMMAND_SUBMITTED,
+ eventData
+ })
+ }
+
+ const handleSubmit = (value?: string) => {
+ sendEventSubmitTelemetry(value)
+ onSubmit(value)
+ }
const Placeholder = (
@@ -34,7 +73,7 @@ const QueryWrapper = (props: Props) => {
setQuery={setQuery}
setQueryEl={setQueryEl}
onKeyDown={onKeyDown}
- onSubmit={onSubmit}
+ onSubmit={handleSubmit}
/>
)
}
diff --git a/redisinsight/ui/src/pages/home/components/DatabasesListComponent/DatabasesList/DatabasesList.tsx b/redisinsight/ui/src/pages/home/components/DatabasesListComponent/DatabasesList/DatabasesList.tsx
index aba608aa5b..3301e5eee5 100644
--- a/redisinsight/ui/src/pages/home/components/DatabasesListComponent/DatabasesList/DatabasesList.tsx
+++ b/redisinsight/ui/src/pages/home/components/DatabasesListComponent/DatabasesList/DatabasesList.tsx
@@ -1,4 +1,6 @@
import {
+ Direction,
+ Criteria,
EuiBasicTableColumn,
EuiButton,
EuiFlexGroup,
@@ -44,7 +46,6 @@ function DatabasesList({
}: Props) {
const [columns, setColumns] = useState(first(columnVariations))
const [selection, setSelection] = useState([])
-
const [isPopoverOpen, setIsPopoverOpen] = useState(false)
const { loading, data: instances } = useSelector(instancesSelector)
@@ -112,6 +113,19 @@ function DatabasesList({
}),
})
+ const onTableChange = ({ sort, page }: Criteria) => {
+ // calls also with page changing
+ if (sort && !page) {
+ sendEventSortedTelemetry(sort)
+ }
+ }
+
+ const sendEventSortedTelemetry = (sort: { field: keyof Instance; direction: Direction }) =>
+ sendEventTelemetry({
+ event: TelemetryEvent.CONFIG_DATABASES_DATABASE_LIST_SORTED,
+ eventData: sort
+ })
+
const deleteBtn = (
diff --git a/redisinsight/ui/src/styles/base/_monaco.scss b/redisinsight/ui/src/styles/base/_monaco.scss
index fad89d7547..06d1c62e41 100644
--- a/redisinsight/ui/src/styles/base/_monaco.scss
+++ b/redisinsight/ui/src/styles/base/_monaco.scss
@@ -16,3 +16,23 @@
font-size: 1.6em;
}
}
+
+.monaco-editor.redisLanguage-editor .suggest-icon {
+ display: none !important;
+}
+
+.monaco-glyph-run-command {
+ color: var(--rsSubmitBtn);
+ opacity: .5 !important;
+ margin-left: 10px;
+
+ &::before {
+ content: '';
+ width: 12px;
+ height: 12px;
+ background-image: url('uiSrc/assets/img/play_icon.svg');
+ background-size: contain;
+ font-size: 16px;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+ }
+}
diff --git a/redisinsight/ui/src/telemetry/events.ts b/redisinsight/ui/src/telemetry/events.ts
index ea94ed9ef7..2911f670dc 100644
--- a/redisinsight/ui/src/telemetry/events.ts
+++ b/redisinsight/ui/src/telemetry/events.ts
@@ -7,6 +7,7 @@ export enum TelemetryEvent {
CONFIG_DATABASES_MULTIPLE_DATABASES_DELETE_CLICKED = 'CONFIG_DATABASES_MULTIPLE_DATABASES_DELETE_CLICKED',
CONFIG_DATABASES_DATABASE_EDIT_CLICKED = 'CONFIG_DATABASES_DATABASE_EDIT_CLICKED',
CONFIG_DATABASES_DATABASE_EDIT_CANCELLED_CLICKED = 'CONFIG_DATABASES_DATABASE_EDIT_CANCELLED_CLICKED',
+ CONFIG_DATABASES_DATABASE_LIST_SORTED = 'CONFIG_DATABASES_DATABASE_LIST_SORTED',
CONFIG_DATABASES_OPEN_DATABASE_BUTTON_CLICKED = 'CONFIG_DATABASES_OPEN_DATABASE_BUTTON_CLICKED',
CONFIG_DATABASES_HOST_PORT_COPIED = 'CONFIG_DATABASES_HOST_PORT_COPIED',
diff --git a/redisinsight/ui/src/utils/index.ts b/redisinsight/ui/src/utils/index.ts
index cce24124dd..677b511f67 100644
--- a/redisinsight/ui/src/utils/index.ts
+++ b/redisinsight/ui/src/utils/index.ts
@@ -27,6 +27,8 @@ export * from './formatBytes'
export * from './instanceModules'
export * from './monacoRedisComplitionProvider'
export * from './monacoRedisMonarchTokensProvider'
+export * from './monacoActions'
+export * from './monacoDecorations'
export * from './handlePlatforms'
export * from './plugins'
diff --git a/redisinsight/ui/src/utils/monacoActions.ts b/redisinsight/ui/src/utils/monacoActions.ts
new file mode 100644
index 0000000000..b66a6850cf
--- /dev/null
+++ b/redisinsight/ui/src/utils/monacoActions.ts
@@ -0,0 +1,22 @@
+import * as monacoEditor from 'monaco-editor'
+
+export enum MonacoAction {
+ Submit = 'submit'
+}
+
+export const geMonacoAction = (
+ actionId: MonacoAction,
+ action: (editor: monacoEditor.editor.IStandaloneCodeEditor, ...args: any[]) => void | Promise,
+ monaco: typeof monacoEditor,
+): monacoEditor.editor.IActionDescriptor => {
+ if (actionId === MonacoAction.Submit) {
+ return {
+ id: 'submit',
+ label: 'Run Commands',
+ keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
+ run: action
+ }
+ }
+
+ return { id: '', label: '', run: () => {} }
+}
diff --git a/redisinsight/ui/src/utils/monacoDecorations.ts b/redisinsight/ui/src/utils/monacoDecorations.ts
new file mode 100644
index 0000000000..6fa1695728
--- /dev/null
+++ b/redisinsight/ui/src/utils/monacoDecorations.ts
@@ -0,0 +1,30 @@
+import * as monacoEditor from 'monaco-editor/esm/vs/editor/editor.api'
+
+export interface ILightWeightDecoration {
+ id: string
+ range: monacoEditor.IRange
+}
+
+interface IModelDeltaDecoration extends monacoEditor.editor.IModelDeltaDecoration {}
+
+export const toModelDeltaDecoration = (dec: ILightWeightDecoration):IModelDeltaDecoration => ({
+ range: dec.range,
+ options: {
+ className: dec.id,
+ isWholeLine: false,
+ glyphMarginClassName: 'monaco-glyph-run-command',
+ // glyphMarginHoverMessage: { value: 'Run command' }
+ }
+})
+
+export const decoration = (
+ monaco: typeof monacoEditor,
+ id: string,
+ startLineNumber: number,
+ startColumn: number,
+ endLineNumber: number,
+ endColum: number
+): ILightWeightDecoration => ({
+ id,
+ range: new monaco.Range(startLineNumber, startColumn, endLineNumber, endColum)
+})
diff --git a/redisinsight/ui/src/utils/test-utils.tsx b/redisinsight/ui/src/utils/test-utils.tsx
index 904fedc130..ec73768284 100644
--- a/redisinsight/ui/src/utils/test-utils.tsx
+++ b/redisinsight/ui/src/utils/test-utils.tsx
@@ -25,11 +25,13 @@ import { initialState as initialStateNotifications } from 'uiSrc/slices/app/noti
import { initialState as initialStateAppInfo } from 'uiSrc/slices/app/info'
import { initialState as initialStateAppContext } from 'uiSrc/slices/app/context'
import { initialState as initialStateAppRedisCommands } from 'uiSrc/slices/app/redis-commands'
+import { initialState as initialStateAppPluginsReducer } from 'uiSrc/slices/app/plugins'
import { initialState as initialStateCliSettings } from 'uiSrc/slices/cli/cli-settings'
import { initialState as initialStateCliOutput } from 'uiSrc/slices/cli/cli-output'
import { initialState as initialStateUserSettings } from 'uiSrc/slices/user/user-settings'
import { initialState as initialStateWBResults } from 'uiSrc/slices/workbench/wb-results'
import { initialState as initialStateWBSettings } from 'uiSrc/slices/workbench/wb-settings'
+import { initialState as initialStateWBEnablementArea } from 'uiSrc/slices/workbench/wb-enablement-area'
interface Options {
initialState?: RootState;
@@ -44,7 +46,8 @@ const initialStateDefault: RootState = {
info: cloneDeep(initialStateAppInfo),
notifications: cloneDeep(initialStateNotifications),
context: cloneDeep(initialStateAppContext),
- redisCommands: cloneDeep(initialStateAppRedisCommands)
+ redisCommands: cloneDeep(initialStateAppRedisCommands),
+ plugins: cloneDeep(initialStateAppPluginsReducer)
},
connections: {
instances: cloneDeep(initialStateInstances),
@@ -73,6 +76,7 @@ const initialStateDefault: RootState = {
workbench: {
results: cloneDeep(initialStateWBResults),
settings: cloneDeep(initialStateWBSettings),
+ enablementArea: cloneDeep(initialStateWBEnablementArea),
},
}