diff --git a/ui/package-lock.json b/ui/package-lock.json index ef9303c380..e1092c81d6 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -26,6 +26,8 @@ "highlight.js": "^11.8.0", "lodash": "^4.17.21", "moment": "^2.29.4", + "monaco-editor": "^0.40.0", + "monaco-themes": "^0.4.4", "nightwind": "^1.1.13", "playwright": "^1.37.1", "react": "^18.2.0", @@ -7108,6 +7110,11 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-plist": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/fast-plist/-/fast-plist-0.1.3.tgz", + "integrity": "sha512-d9cEfo/WcOezgPLAC/8t8wGb6YOD6JTCPMw2QcG2nAdFmyY+9rTUizCTaGjIZAloWENTEUMAPpkUAIJJJ0i96A==" + }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -10151,6 +10158,19 @@ "node": "*" } }, + "node_modules/monaco-editor": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.40.0.tgz", + "integrity": "sha512-1wymccLEuFSMBvCk/jT1YDW/GuxMLYwnFwF9CDyYCxoTw2Pt379J3FUhwy9c43j51JdcxVPjwk0jm0EVDsBS2g==" + }, + "node_modules/monaco-themes": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/monaco-themes/-/monaco-themes-0.4.4.tgz", + "integrity": "sha512-Hbb9pvRrpSi0rZezcB/IOdQnpx10o55Lx4zFdRAAVpFMa1HP7FgaqEZdKffb4ovd90fETCixeFO9JPYFMAq+TQ==", + "dependencies": { + "fast-plist": "^0.1.3" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -17954,6 +17974,11 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "fast-plist": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/fast-plist/-/fast-plist-0.1.3.tgz", + "integrity": "sha512-d9cEfo/WcOezgPLAC/8t8wGb6YOD6JTCPMw2QcG2nAdFmyY+9rTUizCTaGjIZAloWENTEUMAPpkUAIJJJ0i96A==" + }, "fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -20162,6 +20187,19 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" }, + "monaco-editor": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.40.0.tgz", + "integrity": "sha512-1wymccLEuFSMBvCk/jT1YDW/GuxMLYwnFwF9CDyYCxoTw2Pt379J3FUhwy9c43j51JdcxVPjwk0jm0EVDsBS2g==" + }, + "monaco-themes": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/monaco-themes/-/monaco-themes-0.4.4.tgz", + "integrity": "sha512-Hbb9pvRrpSi0rZezcB/IOdQnpx10o55Lx4zFdRAAVpFMa1HP7FgaqEZdKffb4ovd90fETCixeFO9JPYFMAq+TQ==", + "requires": { + "fast-plist": "^0.1.3" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/ui/package.json b/ui/package.json index 292c1f535c..036258e50a 100644 --- a/ui/package.json +++ b/ui/package.json @@ -30,6 +30,8 @@ "highlight.js": "^11.8.0", "lodash": "^4.17.21", "moment": "^2.29.4", + "monaco-editor": "^0.40.0", + "monaco-themes": "^0.4.4", "nightwind": "^1.1.13", "playwright": "^1.37.1", "react": "^18.2.0", diff --git a/ui/src/app/console/Console.tsx b/ui/src/app/console/Console.tsx index 4d88a56eb4..a003e99cd1 100644 --- a/ui/src/app/console/Console.tsx +++ b/ui/src/app/console/Console.tsx @@ -1,18 +1,19 @@ +import { ArrowPathIcon } from '@heroicons/react/20/solid'; import { Form, Formik, useFormikContext } from 'formik'; import hljs from 'highlight.js'; import javascript from 'highlight.js/lib/languages/json'; -import 'highlight.js/styles/tokyo-night-dark.css'; +import 'highlight.js/styles/tomorrow-night-bright.css'; import React, { useCallback, useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import { useNavigate } from 'react-router-dom'; import { v4 as uuidv4 } from 'uuid'; import * as Yup from 'yup'; import { selectCurrentNamespace } from '~/app/namespaces/namespacesSlice'; +import { ContextEditor } from '~/components/console/ContextEditor'; import EmptyState from '~/components/EmptyState'; import Button from '~/components/forms/buttons/Button'; import Combobox from '~/components/forms/Combobox'; import Input from '~/components/forms/Input'; -import TextArea from '~/components/forms/TextArea'; import { evaluateV2, listFlags } from '~/data/api'; import { useError } from '~/data/hooks/error'; import { @@ -27,7 +28,7 @@ import { IFlagList } from '~/types/Flag'; import { INamespace } from '~/types/Namespace'; -import { classNames } from '~/utils/helpers'; +import { classNames, getErrorMessage } from '~/utils/helpers'; hljs.registerLanguage('json', javascript); @@ -44,12 +45,12 @@ function ResetOnNamespaceChange({ namespace }: { namespace: INamespace }) { interface ConsoleFormValues { flagKey: string; entityId: string; - context: string | undefined; } export default function Console() { const [flags, setFlags] = useState([]); const [selectedFlag, setSelectedFlag] = useState(null); + const [context, setContext] = useState(null); const [response, setResponse] = useState(null); const [hasEvaluationError, setHasEvaluationError] = useState(false); @@ -77,14 +78,21 @@ export default function Console() { }, [namespace.key]); const handleSubmit = (flag: IFlag | null, values: ConsoleFormValues) => { - const { entityId, context } = values; + const { entityId } = values; if (!flag) { return; } - // need to unescape the context string - const parsed = context ? JSON.parse(context) : undefined; + let parsed = null; + try { + // need to unescape the context string + parsed = context ? JSON.parse(context) : undefined; + } catch (err) { + setHasEvaluationError(true); + setResponse('error: ' + getErrorMessage(err)); + return; + } const rest = { entityId, @@ -116,8 +124,7 @@ export default function Console() { const initialvalues: ConsoleFormValues = { flagKey: selectedFlag?.key || '', - entityId: uuidv4(), - context: undefined + entityId: uuidv4() }; return ( @@ -144,11 +151,6 @@ export default function Console() { onSubmit={(values) => { handleSubmit(selectedFlag, values); }} - onReset={() => { - setResponse(null); - setHasEvaluationError(false); - setSelectedFlag(null); - }} > {(formik) => (
@@ -178,12 +180,29 @@ export default function Console() { > Entity ID - +
+ + +
-