diff --git a/internal/webui/package.json b/internal/webui/package.json index 1f9e3c054a..18bd33cd45 100644 --- a/internal/webui/package.json +++ b/internal/webui/package.json @@ -45,11 +45,13 @@ "axios": "^1.15.0", "classnames": "^2.5.1", "form-data": "^4.0.0", + "prismjs": "^1.30.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.55.0", "react-markdown": "^10.1.0", "react-router-dom": "^6.28.1", + "react-simple-code-editor": "^0.14.1", "react-syntax-highlighter": "^16.1.1", "react-use-websocket": "^4.5.0", "tss-react": "^4.9.6", @@ -60,6 +62,7 @@ "devDependencies": { "@eslint/js": "^9.27.0", "@types/node": "^22.15.0", + "@types/prismjs": "^1.26.6", "@types/react": "^18.3.28", "@types/react-dom": "^18.3.7", "@types/react-syntax-highlighter": "^15.5.13", diff --git a/internal/webui/src/App.tsx b/internal/webui/src/App.tsx index 07a5b7ff0b..e45655191d 100644 --- a/internal/webui/src/App.tsx +++ b/internal/webui/src/App.tsx @@ -13,6 +13,9 @@ import { privateRoutes, publicRoutes } from "layout/Routes"; import { AppBar } from "./layout/AppBar"; +import "prismjs/components/prism-sql"; +import "prismjs/themes/prism.css"; + const mdTheme = createTheme(); export default function App() { diff --git a/internal/webui/src/containers/MetricFormDialog/components/MetricForm/components/MetricFormStepSQL.tsx b/internal/webui/src/containers/MetricFormDialog/components/MetricForm/components/MetricFormStepSQL.tsx index cfde2d1c6e..1016b71a67 100644 --- a/internal/webui/src/containers/MetricFormDialog/components/MetricForm/components/MetricFormStepSQL.tsx +++ b/internal/webui/src/containers/MetricFormDialog/components/MetricForm/components/MetricFormStepSQL.tsx @@ -1,45 +1,63 @@ -import { FormControl, FormHelperText, InputLabel, OutlinedInput } from "@mui/material"; -import { useFormContext } from "react-hook-form"; +import { FormControl, FormHelperText, InputLabel, OutlinedInput, useTheme } from "@mui/material"; +import { Controller, useFormContext } from "react-hook-form"; import { useFormStyles } from "styles/form"; import { MetricFormValues } from "../MetricForm.types"; +import Editor from "react-simple-code-editor"; +import { highlight, languages } from "prismjs"; + + export const MetricFormStepSQL = () => { - const { register, formState: { errors } } = useFormContext(); + const { control, formState: { errors } } = useFormContext(); const { classes, cx } = useFormStyles(); + const theme = useTheme(); - const hasError = (field: keyof MetricFormValues) => !!errors[field]; - - const getError = (field: keyof MetricFormValues) => { - const error = errors[field]; - if (error) { - return error.message; - } - return undefined; - }; + const hasError = !!errors.SQLs; + const errorMessage = errors.SQLs?.message; return (
- SQLs - + SQLs + + ( + highlight(code, languages.sql, "sql")} + padding={12} + id="InitSQL" + aria-describedby="InitSQL-error" + style={{ + fontFamily: "'Fira Code', 'Consolas', monospace", + fontSize: "0.75rem", + lineHeight: 1.6, + minHeight: "240px", + borderRadius: "4px", + backgroundColor: theme.palette.background.paper, + color: theme.palette.text.primary, + border: hasError + ? `1px solid ${theme.palette.error.main}` + : `1px solid ${theme.palette.divider}`, + }} + /> + )} /> - {getError("SQLs")} + {errorMessage}
); diff --git a/internal/webui/src/containers/MetricFormDialog/components/MetricForm/components/MetricFormStepSettings.tsx b/internal/webui/src/containers/MetricFormDialog/components/MetricForm/components/MetricFormStepSettings.tsx index f7bb9a6496..201ee10e1d 100644 --- a/internal/webui/src/containers/MetricFormDialog/components/MetricForm/components/MetricFormStepSettings.tsx +++ b/internal/webui/src/containers/MetricFormDialog/components/MetricForm/components/MetricFormStepSettings.tsx @@ -1,19 +1,26 @@ -import { Checkbox, FormControl, FormControlLabel, FormHelperText, InputLabel, OutlinedInput } from "@mui/material"; -import { useController, useFormContext } from "react-hook-form"; +import { Checkbox, FormControl, FormControlLabel, FormHelperText, InputLabel, OutlinedInput, useTheme } from "@mui/material"; +import { Controller, useController, useFormContext } from "react-hook-form"; import { useFormStyles } from "styles/form"; import { MetricFormValues } from "../MetricForm.types"; +import Editor from "react-simple-code-editor"; +import { highlight, languages } from "prismjs"; + export const MetricFormStepSettings = () => { - const { register, control } = useFormContext(); + const { register, control, formState: { errors }, } = useFormContext(); const { classes, cx } = useFormStyles(); - - const { field } = useController({ name: "IsInstanceLevel", control }); + const theme = useTheme(); + const hasError = !!errors.SQLs; + const errorMessage = errors.SQLs?.message; + const { field: instanceLevelField } = useController({ name: "IsInstanceLevel", control }); return (
Gauges { - Init SQL - + Init SQL + + ( + highlight(code, languages.sql, "sql")} + padding={12} + id="InitSQL" + style={{ + fontFamily: "'Fira Code', 'Consolas', monospace", + fontSize: "0.75rem", + lineHeight: 1.6, + minHeight: "120px", + borderRadius: "4px", + backgroundColor: theme.palette.background.paper, + color: theme.palette.text.primary, + border: hasError + ? `1px solid ${theme.palette.error.main}` + : `1px solid ${theme.palette.divider}`, + }} + /> + )} /> + {errorMessage} { labelPlacement="start" control={ } /> diff --git a/internal/webui/tsconfig.json b/internal/webui/tsconfig.json index e228625730..9104a3fde3 100644 --- a/internal/webui/tsconfig.json +++ b/internal/webui/tsconfig.json @@ -25,6 +25,7 @@ "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, + "ignoreDeprecations": "6.0", "downlevelIteration": true, "noFallthroughCasesInSwitch": true, "jsx": "react-jsx" diff --git a/internal/webui/yarn.lock b/internal/webui/yarn.lock index 940e0fd8fe..cb7f7f0fb7 100644 --- a/internal/webui/yarn.lock +++ b/internal/webui/yarn.lock @@ -1013,7 +1013,7 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== -"@types/prismjs@^1.0.0": +"@types/prismjs@^1.0.0", "@types/prismjs@^1.26.6": version "1.26.6" resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.26.6.tgz#6ea27c126d645319ae4f7055eda63a9e835c0187" integrity sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw== @@ -3428,6 +3428,11 @@ react-router@6.30.3: dependencies: "@remix-run/router" "1.23.2" +react-simple-code-editor@^0.14.1: + version "0.14.1" + resolved "https://registry.yarnpkg.com/react-simple-code-editor/-/react-simple-code-editor-0.14.1.tgz#fd37eb3349f5def45900dd46acf296f796d81d2c" + integrity sha512-BR5DtNRy+AswWJECyA17qhUDvrrCZ6zXOCfkQY5zSmb96BVUbpVAv03WpcjcwtCwiLbIANx3gebHOcXYn1EHow== + react-syntax-highlighter@^16.1.1: version "16.1.1" resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-16.1.1.tgz#928459855d375f5cfc8e646071e20d541cebcb52"