From c016753f87cc18b69cbf04e3d869b91f4189036d Mon Sep 17 00:00:00 2001 From: Tahier Hussain Date: Fri, 22 Nov 2024 16:23:55 +0530 Subject: [PATCH 01/18] Add markdown rendering support to WF and PS log components --- frontend/src/components/agency/display-logs/DisplayLogs.jsx | 3 ++- .../src/components/custom-tools/display-logs/DisplayLogs.jsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/agency/display-logs/DisplayLogs.jsx b/frontend/src/components/agency/display-logs/DisplayLogs.jsx index 93cb7aca7..8afb4e1b2 100644 --- a/frontend/src/components/agency/display-logs/DisplayLogs.jsx +++ b/frontend/src/components/agency/display-logs/DisplayLogs.jsx @@ -1,5 +1,6 @@ import { useEffect, useRef } from "react"; import { Col, Row, Typography } from "antd"; +import ReactMarkdown from "markdown-to-jsx"; import "./DisplayLogs.css"; import { useSocketLogsStore } from "../../../store/socket-logs-store"; @@ -38,7 +39,7 @@ function DisplayLogs() { - {log?.message} + {log?.message} diff --git a/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx b/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx index f0f6b7bd0..6fe89195b 100644 --- a/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx +++ b/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx @@ -1,5 +1,6 @@ import { useEffect, useRef } from "react"; import { Col, Row, Typography } from "antd"; +import ReactMarkdown from "markdown-to-jsx"; import "../../agency/display-logs/DisplayLogs.css"; import { useSocketCustomToolStore } from "../../../store/socket-custom-tool"; @@ -49,7 +50,7 @@ function DisplayLogs() { - {message?.message} + {message?.message} From 11f3fa901efbfb1e6c406b99f12f9e71571d7ad1 Mon Sep 17 00:00:00 2001 From: Tahier Hussain Date: Fri, 22 Nov 2024 16:34:22 +0530 Subject: [PATCH 02/18] Add markdown rendering support to API Deployment logs component --- .../pipelines-or-deployments/log-modal/LogsModal.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/pipelines-or-deployments/log-modal/LogsModal.jsx b/frontend/src/components/pipelines-or-deployments/log-modal/LogsModal.jsx index a41f9e041..a5982920f 100644 --- a/frontend/src/components/pipelines-or-deployments/log-modal/LogsModal.jsx +++ b/frontend/src/components/pipelines-or-deployments/log-modal/LogsModal.jsx @@ -1,6 +1,7 @@ import { Table, Modal, Button } from "antd"; import PropTypes from "prop-types"; import { useState } from "react"; +import ReactMarkdown from "markdown-to-jsx"; import { useSessionStore } from "../../../store/session-store.js"; import { useAxiosPrivate } from "../../../hooks/useAxiosPrivate.js"; @@ -40,7 +41,7 @@ const LogsModal = ({ .then((res) => { const logDetails = res.data.results.map((item) => ({ id: item.id, - log: item.data?.log, + log: {item.data?.log}, type: item.data?.type, stage: item.data?.stage, level: item.data?.level, From 69c0277d4a6b0b570f44f598826057356e62384b Mon Sep 17 00:00:00 2001 From: Tahier Hussain Date: Sun, 24 Nov 2024 15:40:47 +0530 Subject: [PATCH 03/18] New component to render markdown only for bold, link and new line --- .../custom-markdown/CustomMarkdown.jsx | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 frontend/src/components/helpers/custom-markdown/CustomMarkdown.jsx diff --git a/frontend/src/components/helpers/custom-markdown/CustomMarkdown.jsx b/frontend/src/components/helpers/custom-markdown/CustomMarkdown.jsx new file mode 100644 index 000000000..c6d38e0f5 --- /dev/null +++ b/frontend/src/components/helpers/custom-markdown/CustomMarkdown.jsx @@ -0,0 +1,127 @@ +import React from "react"; +import { Typography } from "antd"; +import PropTypes from "prop-types"; + +const { Text, Link } = Typography; + +const parseMarkdown = (input, renderNewLines, isSecondary, styleClassName) => { + const elements = []; + let index = 0; + + // Define style and type for text elements + const textType = isSecondary ? "secondary" : undefined; + const className = styleClassName ? styleClassName : ""; + + while (index < input?.length) { + if (input?.startsWith("**", index)) { + // Handle bold text + const endIndex = input.indexOf("**", index + 2); + if (endIndex !== -1) { + const boldText = input.substring(index + 2, endIndex); + elements.push( + + {boldText} + + ); + index = endIndex + 2; + } else { + // No closing '**', treat as regular text + elements.push(input.substring(index, index + 2)); + index += 2; + } + } else if (input?.[index] === "[") { + // Handle links + const endLinkTextIndex = input.indexOf("]", index); + const startUrlIndex = input.indexOf("(", endLinkTextIndex); + const endUrlIndex = input.indexOf(")", startUrlIndex); + + if ( + endLinkTextIndex !== -1 && + startUrlIndex === endLinkTextIndex + 1 && + endUrlIndex !== -1 + ) { + const linkText = input.substring(index + 1, endLinkTextIndex); + const url = input.substring(startUrlIndex + 1, endUrlIndex); + elements.push( + + {linkText} + + ); + index = endUrlIndex + 1; + } else { + // Not a valid link syntax, treat as regular text + elements.push(input?.[index]); + index += 1; + } + } else if (input?.[index] === "\n") { + // Handle new lines based on the renderNewLines prop + if (renderNewLines) { + elements.push(
); + } else { + elements.push("\n"); + } + index += 1; + } else { + // Handle regular text + let nextIndex = input.indexOf("**", index); + const linkIndex = input.indexOf("[", index); + const newLineIndex = input.indexOf("\n", index); + + // Find the next markdown syntax or end of string + const indices = [nextIndex, linkIndex, newLineIndex].filter( + (i) => i !== -1 + ); + nextIndex = indices.length > 0 ? Math.min(...indices) : -1; + + if (nextIndex === -1) { + elements.push(input.substring(index)); + break; + } else { + elements.push(input.substring(index, nextIndex)); + index = nextIndex; + } + } + } + + return elements; +}; + +const CustomMarkdown = ({ + text = "", + renderNewLines = true, + isSecondary = false, + styleClassName, +}) => { + const content = React.useMemo( + () => parseMarkdown(text, renderNewLines, isSecondary, styleClassName), + [text, renderNewLines, isSecondary] + ); + + const textType = isSecondary ? "secondary" : undefined; + + return ( + + {content} + + ); +}; + +CustomMarkdown.propTypes = { + text: PropTypes.string, + renderNewLines: PropTypes.bool, + isSecondary: PropTypes.bool, + styleClassName: PropTypes.string, +}; + +export default CustomMarkdown; From 86cde0a8c0d9f7eb0befc229b1d9c01446af5c7b Mon Sep 17 00:00:00 2001 From: Tahier Hussain Date: Sun, 24 Nov 2024 15:42:38 +0530 Subject: [PATCH 04/18] Use the new CustomMarkdown component for rendering logs and notification message --- frontend/src/App.jsx | 3 ++- .../src/components/agency/display-logs/DisplayLogs.jsx | 10 ++++++---- .../custom-tools/display-logs/DisplayLogs.jsx | 10 ++++++---- .../pipelines-or-deployments/log-modal/LogsModal.jsx | 4 ++-- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 9fbf63bee..734be4ad7 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -7,6 +7,7 @@ import { useAlertStore } from "./store/alert-store.js"; import { useSessionStore } from "./store/session-store.js"; import PostHogPageviewTracker from "./PostHogPageviewTracker.js"; import { useEffect } from "react"; +import CustomMarkdown from "./components/helpers/custom-markdown/CustomMarkdown.jsx"; let GoogleTagManagerHelper; try { @@ -46,7 +47,7 @@ function App() { notificationAPI.open({ message: alertDetails?.title, - description: alertDetails?.content, + description: , type: alertDetails?.type, duration: alertDetails?.duration, btn, diff --git a/frontend/src/components/agency/display-logs/DisplayLogs.jsx b/frontend/src/components/agency/display-logs/DisplayLogs.jsx index 8afb4e1b2..580671120 100644 --- a/frontend/src/components/agency/display-logs/DisplayLogs.jsx +++ b/frontend/src/components/agency/display-logs/DisplayLogs.jsx @@ -1,9 +1,9 @@ import { useEffect, useRef } from "react"; import { Col, Row, Typography } from "antd"; -import ReactMarkdown from "markdown-to-jsx"; import "./DisplayLogs.css"; import { useSocketLogsStore } from "../../../store/socket-logs-store"; +import CustomMarkdown from "../../helpers/custom-markdown/CustomMarkdown"; function DisplayLogs() { const bottomRef = useRef(null); @@ -38,9 +38,11 @@ function DisplayLogs() { - - {log?.message} - + diff --git a/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx b/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx index 6fe89195b..ff9297663 100644 --- a/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx +++ b/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx @@ -1,10 +1,10 @@ import { useEffect, useRef } from "react"; import { Col, Row, Typography } from "antd"; -import ReactMarkdown from "markdown-to-jsx"; import "../../agency/display-logs/DisplayLogs.css"; import { useSocketCustomToolStore } from "../../../store/socket-custom-tool"; import { getDateTimeString } from "../../../helpers/GetStaticData"; +import CustomMarkdown from "../../helpers/custom-markdown/CustomMarkdown"; function DisplayLogs() { const bottomRef = useRef(null); @@ -49,9 +49,11 @@ function DisplayLogs() { - - {message?.message} - +
diff --git a/frontend/src/components/pipelines-or-deployments/log-modal/LogsModal.jsx b/frontend/src/components/pipelines-or-deployments/log-modal/LogsModal.jsx index a5982920f..478be9248 100644 --- a/frontend/src/components/pipelines-or-deployments/log-modal/LogsModal.jsx +++ b/frontend/src/components/pipelines-or-deployments/log-modal/LogsModal.jsx @@ -1,13 +1,13 @@ import { Table, Modal, Button } from "antd"; import PropTypes from "prop-types"; import { useState } from "react"; -import ReactMarkdown from "markdown-to-jsx"; import { useSessionStore } from "../../../store/session-store.js"; import { useAxiosPrivate } from "../../../hooks/useAxiosPrivate.js"; import { useAlertStore } from "../../../store/alert-store.js"; import { useExceptionHandler } from "../../../hooks/useExceptionHandler.jsx"; import "./LogsModel.css"; +import CustomMarkdown from "../../helpers/custom-markdown/CustomMarkdown.jsx"; const LogsModal = ({ open, @@ -41,7 +41,7 @@ const LogsModal = ({ .then((res) => { const logDetails = res.data.results.map((item) => ({ id: item.id, - log: {item.data?.log}, + log: , type: item.data?.type, stage: item.data?.stage, level: item.data?.level, From 067ee26fbd77002bf74661c8ff5f05166a5c5fac Mon Sep 17 00:00:00 2001 From: Tahier Hussain Date: Mon, 25 Nov 2024 14:16:43 +0530 Subject: [PATCH 05/18] Optimize the CustomMarkdown component --- .../custom-markdown/CustomMarkdown.jsx | 163 +++++++++++------- 1 file changed, 104 insertions(+), 59 deletions(-) diff --git a/frontend/src/components/helpers/custom-markdown/CustomMarkdown.jsx b/frontend/src/components/helpers/custom-markdown/CustomMarkdown.jsx index c6d38e0f5..f354b1ab8 100644 --- a/frontend/src/components/helpers/custom-markdown/CustomMarkdown.jsx +++ b/frontend/src/components/helpers/custom-markdown/CustomMarkdown.jsx @@ -4,17 +4,63 @@ import PropTypes from "prop-types"; const { Text, Link } = Typography; -const parseMarkdown = (input, renderNewLines, isSecondary, styleClassName) => { - const elements = []; - let index = 0; - - // Define style and type for text elements +const CustomMarkdown = ({ + text = "", + renderNewLines = true, + isSecondary = false, + styleClassName, +}) => { const textType = isSecondary ? "secondary" : undefined; - const className = styleClassName ? styleClassName : ""; + const className = styleClassName || ""; + + const parseMarkdown = React.useCallback(() => { + const elements = []; + let index = 0; + const input = text; + + // Helper functions for parsing different markdown elements + const parseCodeBlock = () => { + const endIndex = input.indexOf("```", index + 3); + if (endIndex !== -1) { + const codeText = input.substring(index + 3, endIndex); + elements.push( + + {codeText} + + ); + index = endIndex + 3; + return true; + } + return false; + }; + + const parseInlineCode = () => { + const endIndex = input.indexOf("`", index + 1); + if (endIndex !== -1) { + const codeText = input.substring(index + 1, endIndex); + elements.push( + + {codeText} + + ); + index = endIndex + 1; + return true; + } + return false; + }; - while (index < input?.length) { - if (input?.startsWith("**", index)) { - // Handle bold text + const parseBoldText = () => { const endIndex = input.indexOf("**", index + 2); if (endIndex !== -1) { const boldText = input.substring(index + 2, endIndex); @@ -29,13 +75,12 @@ const parseMarkdown = (input, renderNewLines, isSecondary, styleClassName) => { ); index = endIndex + 2; - } else { - // No closing '**', treat as regular text - elements.push(input.substring(index, index + 2)); - index += 2; + return true; } - } else if (input?.[index] === "[") { - // Handle links + return false; + }; + + const parseLink = () => { const endLinkTextIndex = input.indexOf("]", index); const startUrlIndex = input.indexOf("(", endLinkTextIndex); const endUrlIndex = input.indexOf(")", startUrlIndex); @@ -59,59 +104,59 @@ const parseMarkdown = (input, renderNewLines, isSecondary, styleClassName) => { ); index = endUrlIndex + 1; - } else { - // Not a valid link syntax, treat as regular text - elements.push(input?.[index]); - index += 1; + return true; } - } else if (input?.[index] === "\n") { - // Handle new lines based on the renderNewLines prop - if (renderNewLines) { - elements.push(
); - } else { - elements.push("\n"); + return false; + }; + + while (index < input.length) { + const char = input[index]; + + if (input.startsWith("```", index)) { + if (parseCodeBlock()) continue; + } else if (input.startsWith("`", index)) { + if (parseInlineCode()) continue; + } else if (input.startsWith("**", index)) { + if (parseBoldText()) continue; + } else if (char === "[") { + if (parseLink()) continue; + } else if (char === "\n") { + // Handle new lines + if (renderNewLines) { + elements.push(
); + } else { + elements.push("\n"); + } + index += 1; + continue; } - index += 1; - } else { + // Handle regular text - let nextIndex = input.indexOf("**", index); - const linkIndex = input.indexOf("[", index); - const newLineIndex = input.indexOf("\n", index); - - // Find the next markdown syntax or end of string - const indices = [nextIndex, linkIndex, newLineIndex].filter( - (i) => i !== -1 - ); - nextIndex = indices.length > 0 ? Math.min(...indices) : -1; - - if (nextIndex === -1) { - elements.push(input.substring(index)); - break; - } else { - elements.push(input.substring(index, nextIndex)); - index = nextIndex; + let nextIndex = input.length; + const nextSpecialIndices = [ + input.indexOf("```", index), + input.indexOf("`", index), + input.indexOf("**", index), + input.indexOf("[", index), + input.indexOf("\n", index), + ].filter((i) => i !== -1); + + if (nextSpecialIndices.length > 0) { + nextIndex = Math.min(...nextSpecialIndices); } - } - } - return elements; -}; + const textSegment = input.substring(index, nextIndex); + elements.push(textSegment); + index = nextIndex; + } -const CustomMarkdown = ({ - text = "", - renderNewLines = true, - isSecondary = false, - styleClassName, -}) => { - const content = React.useMemo( - () => parseMarkdown(text, renderNewLines, isSecondary, styleClassName), - [text, renderNewLines, isSecondary] - ); + return elements; + }, [text, renderNewLines, textType, className]); - const textType = isSecondary ? "secondary" : undefined; + const content = React.useMemo(() => parseMarkdown(), [parseMarkdown]); return ( - + {content} ); From 8fd1e561e33251024dfa6f4579a5f9e16fc8cca4 Mon Sep 17 00:00:00 2001 From: Tahier Hussain Date: Mon, 25 Nov 2024 16:54:31 +0530 Subject: [PATCH 06/18] Support selective markdown formatting for RJSF form's helper text --- .../checkbox-widget/CheckboxWidget.jsx | 14 ++++--- .../select-widget/SelectWidget.jsx | 35 +++++++++++----- .../rjsf-form-layout/CustomFieldTemplate.jsx | 21 ++++++++++ .../rjsf-form-layout/RjsfFormLayout.css | 6 ++- .../rjsf-form-layout/RjsfFormLayout.jsx | 41 +++++++++++-------- .../rjsf-widget-layout/RjsfWidgetLayout.jsx | 16 ++++---- 6 files changed, 92 insertions(+), 41 deletions(-) create mode 100644 frontend/src/layouts/rjsf-form-layout/CustomFieldTemplate.jsx diff --git a/frontend/src/components/rjsf-custom-widgets/checkbox-widget/CheckboxWidget.jsx b/frontend/src/components/rjsf-custom-widgets/checkbox-widget/CheckboxWidget.jsx index de5a9c4dd..f6c884c5c 100644 --- a/frontend/src/components/rjsf-custom-widgets/checkbox-widget/CheckboxWidget.jsx +++ b/frontend/src/components/rjsf-custom-widgets/checkbox-widget/CheckboxWidget.jsx @@ -1,7 +1,7 @@ -import { QuestionCircleOutlined } from "@ant-design/icons"; -import { Checkbox, Space, Tooltip, Typography } from "antd"; +import { Checkbox, Space, Typography } from "antd"; import PropTypes from "prop-types"; import "./CheckboxWidget.css"; +import CustomMarkdown from "../../helpers/custom-markdown/CustomMarkdown"; const CheckboxWidget = ({ id, value, onChange, label, schema }) => { const description = schema?.description || ""; const handleCheckboxChange = (event) => { @@ -9,14 +9,16 @@ const CheckboxWidget = ({ id, value, onChange, label, schema }) => { }; return ( - + {label} {description?.length > 0 && ( - - - + )} ); diff --git a/frontend/src/components/rjsf-custom-widgets/select-widget/SelectWidget.jsx b/frontend/src/components/rjsf-custom-widgets/select-widget/SelectWidget.jsx index 8b6846591..acc2be756 100644 --- a/frontend/src/components/rjsf-custom-widgets/select-widget/SelectWidget.jsx +++ b/frontend/src/components/rjsf-custom-widgets/select-widget/SelectWidget.jsx @@ -1,9 +1,12 @@ -import { Form, Select } from "antd"; +import { Form, Select, Space, Typography } from "antd"; import PropTypes from "prop-types"; +import CustomMarkdown from "../../helpers/custom-markdown/CustomMarkdown"; const { Option } = Select; const SelectWidget = (props) => { - const { id, value, options, onChange, rawErrors } = props; + const { id, value, options, onChange, label, schema, rawErrors } = props; + const description = schema?.description || ""; + const handleSelectChange = (selectedValue) => { onChange(selectedValue); }; @@ -14,14 +17,24 @@ const SelectWidget = (props) => { style={{ width: "100%" }} validateStatus={hasError ? "error" : ""} > - + + {label} + + {description?.length && ( + + )} + ); }; @@ -32,6 +45,8 @@ SelectWidget.propTypes = { options: PropTypes.any, onChange: PropTypes.func.isRequired, rawErrors: PropTypes.array, + label: PropTypes.string.isRequired, + schema: PropTypes.object.isRequired, }; export { SelectWidget }; diff --git a/frontend/src/layouts/rjsf-form-layout/CustomFieldTemplate.jsx b/frontend/src/layouts/rjsf-form-layout/CustomFieldTemplate.jsx new file mode 100644 index 000000000..598e5cc44 --- /dev/null +++ b/frontend/src/layouts/rjsf-form-layout/CustomFieldTemplate.jsx @@ -0,0 +1,21 @@ +import PropTypes from "prop-types"; + +const CustomFieldTemplate = (props) => { + const { classNames, errors, children, help } = props; + return ( +
+ {children} + {errors} + {help} +
+ ); +}; + +CustomFieldTemplate.propTypes = { + classNames: PropTypes.string, + help: PropTypes.node, + errors: PropTypes.node, + children: PropTypes.node, +}; + +export { CustomFieldTemplate }; diff --git a/frontend/src/layouts/rjsf-form-layout/RjsfFormLayout.css b/frontend/src/layouts/rjsf-form-layout/RjsfFormLayout.css index 8edb72964..46a09663b 100644 --- a/frontend/src/layouts/rjsf-form-layout/RjsfFormLayout.css +++ b/frontend/src/layouts/rjsf-form-layout/RjsfFormLayout.css @@ -1,7 +1,7 @@ /* Styles for RjsfFormLayout */ .rjsf-form-layout-spin { - padding: 10px 0px; + padding: 10px 0px; } .my-rjsf-form .ant-form-item { @@ -19,3 +19,7 @@ padding-right: 0 !important; padding-bottom: 0 !important; } + +.rjsf-helper-font { + font-size: 12px; +} diff --git a/frontend/src/layouts/rjsf-form-layout/RjsfFormLayout.jsx b/frontend/src/layouts/rjsf-form-layout/RjsfFormLayout.jsx index 6c971c680..ea5cd90ff 100644 --- a/frontend/src/layouts/rjsf-form-layout/RjsfFormLayout.jsx +++ b/frontend/src/layouts/rjsf-form-layout/RjsfFormLayout.jsx @@ -17,6 +17,10 @@ import { SelectWidget } from "../../components/rjsf-custom-widgets/select-widget import { TimeWidget } from "../../components/rjsf-custom-widgets/time-widget/TimeWidget.jsx"; import { URLWidget } from "../../components/rjsf-custom-widgets/url-widget/URLWidget.jsx"; import { SpinnerLoader } from "../../components/widgets/spinner-loader/SpinnerLoader.jsx"; +import { TextWidget } from "../../components/rjsf-custom-widgets/text-widget/TextWidget.jsx"; +import { PasswordWidget } from "../../components/rjsf-custom-widgets/password-widget/PasswordWidget.jsx"; +import { UpDownWidget } from "../../components/rjsf-custom-widgets/up-down-widget/UpDownWidget.jsx"; +import { CustomFieldTemplate } from "./CustomFieldTemplate.jsx"; import "./RjsfFormLayout.css"; function RjsfFormLayout({ @@ -32,23 +36,26 @@ function RjsfFormLayout({ schema.title = ""; schema.description = ""; const widgets = { - CheckboxWidget: CheckboxWidget, - DateWidget: DateWidget, - AltDateTimeWidget: AltDateTimeWidget, - AltDateWidget: AltDateWidget, - CheckboxesWidget: CheckboxesWidget, - ColorWidget: ColorWidget, - DateTimeWidget: DateTimeWidget, - EmailWidget: EmailWidget, - FileWidget: FileWidget, - HiddenWidget: HiddenWidget, - TimeWidget: TimeWidget, - URLWidget: URLWidget, - SelectWidget: SelectWidget, + AltDateTimeWidget, + AltDateWidget, + CheckboxWidget, + CheckboxesWidget, + ColorWidget, + DateTimeWidget, + DateWidget, + EmailWidget, + FileWidget, + HiddenWidget, + PasswordWidget, + SelectWidget, + TextWidget, + TimeWidget, + UpDownWidget, + URLWidget, }; const fields = { - ArrayField: ArrayField, + ArrayField, }; const uiSchema = { @@ -116,11 +123,11 @@ function RjsfFormLayout({ transformErrors={transformErrors} onError={() => {}} onSubmit={(e) => validateAndSubmit(e.formData)} - formContext={{ - descriptionLocation: "tooltip", - }} showErrorList={false} onChange={handleChange} + templates={{ + FieldTemplate: CustomFieldTemplate, + }} > {children} diff --git a/frontend/src/layouts/rjsf-widget-layout/RjsfWidgetLayout.jsx b/frontend/src/layouts/rjsf-widget-layout/RjsfWidgetLayout.jsx index f33b1eb6d..6a6991fbd 100644 --- a/frontend/src/layouts/rjsf-widget-layout/RjsfWidgetLayout.jsx +++ b/frontend/src/layouts/rjsf-widget-layout/RjsfWidgetLayout.jsx @@ -1,7 +1,7 @@ -import { QuestionCircleOutlined } from "@ant-design/icons"; -import { Form, Tooltip, Typography } from "antd"; +import { Form, Typography } from "antd"; import PropTypes from "prop-types"; import "./RjsfWidgetLayout.css"; +import CustomMarkdown from "../../components/helpers/custom-markdown/CustomMarkdown"; function RjsfWidgetLayout({ children, label, description, required }) { return ( @@ -9,13 +9,15 @@ function RjsfWidgetLayout({ children, label, description, required }) { {required && * } {label} - {description?.length > 0 && ( - - - - )} {children} + {description?.length > 0 && ( + + )} ); } From ae677d142f387efeaf264315c044b6678be85459 Mon Sep 17 00:00:00 2001 From: Tahier Hussain Date: Wed, 4 Dec 2024 16:28:05 +0530 Subject: [PATCH 07/18] Add code block support for triple backticks and refine UI elements --- .../agency/display-logs/DisplayLogs.jsx | 1 - .../custom-tools/display-logs/DisplayLogs.jsx | 1 - .../custom-markdown/CustomMarkdown.jsx | 13 +++---- .../log-modal/LogsModal.jsx | 2 +- .../AltDateTimeWidget.jsx | 17 ++++++++- .../alt-date-widget/AltDateWidget.jsx | 11 +++++- .../checkbox-widget/CheckboxWidget.jsx | 8 +++- .../checkboxes-widget/CheckboxesWidget.jsx | 19 +++++++++- .../color-widget/ColorWidget.jsx | 12 +++++- .../date-time-widget/DateTimeWidget.jsx | 10 ++++- .../date-widget/DateWidget.jsx | 10 ++++- .../email-widget/EmailWidget.jsx | 10 ++++- .../file-widget/FileWidget.jsx | 10 ++++- .../select-widget/SelectWidget.jsx | 37 +++++++++++-------- .../time-widget/TimeWidget.jsx | 10 ++++- .../up-down-widget/UpDownWidget.jsx | 10 ++++- .../url-widget/URLWidget.jsx | 22 ++++++----- 17 files changed, 146 insertions(+), 57 deletions(-) diff --git a/frontend/src/components/agency/display-logs/DisplayLogs.jsx b/frontend/src/components/agency/display-logs/DisplayLogs.jsx index 580671120..de456743a 100644 --- a/frontend/src/components/agency/display-logs/DisplayLogs.jsx +++ b/frontend/src/components/agency/display-logs/DisplayLogs.jsx @@ -40,7 +40,6 @@ function DisplayLogs() { diff --git a/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx b/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx index ff9297663..1fa43cc0a 100644 --- a/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx +++ b/frontend/src/components/custom-tools/display-logs/DisplayLogs.jsx @@ -51,7 +51,6 @@ function DisplayLogs() { diff --git a/frontend/src/components/helpers/custom-markdown/CustomMarkdown.jsx b/frontend/src/components/helpers/custom-markdown/CustomMarkdown.jsx index f354b1ab8..fffd535c6 100644 --- a/frontend/src/components/helpers/custom-markdown/CustomMarkdown.jsx +++ b/frontend/src/components/helpers/custom-markdown/CustomMarkdown.jsx @@ -2,7 +2,7 @@ import React from "react"; import { Typography } from "antd"; import PropTypes from "prop-types"; -const { Text, Link } = Typography; +const { Text, Link, Paragraph } = Typography; const CustomMarkdown = ({ text = "", @@ -18,21 +18,18 @@ const CustomMarkdown = ({ let index = 0; const input = text; - // Helper functions for parsing different markdown elements const parseCodeBlock = () => { const endIndex = input.indexOf("```", index + 3); if (endIndex !== -1) { const codeText = input.substring(index + 3, endIndex); elements.push( - - {codeText} - +
{codeText}
+ ); index = endIndex + 3; return true; diff --git a/frontend/src/components/pipelines-or-deployments/log-modal/LogsModal.jsx b/frontend/src/components/pipelines-or-deployments/log-modal/LogsModal.jsx index 478be9248..e750deaeb 100644 --- a/frontend/src/components/pipelines-or-deployments/log-modal/LogsModal.jsx +++ b/frontend/src/components/pipelines-or-deployments/log-modal/LogsModal.jsx @@ -41,7 +41,7 @@ const LogsModal = ({ .then((res) => { const logDetails = res.data.results.map((item) => ({ id: item.id, - log: , + log: , type: item.data?.type, stage: item.data?.stage, level: item.data?.level, diff --git a/frontend/src/components/rjsf-custom-widgets/alt-date-time-widget/AltDateTimeWidget.jsx b/frontend/src/components/rjsf-custom-widgets/alt-date-time-widget/AltDateTimeWidget.jsx index 59f52c4b4..25112fa3b 100644 --- a/frontend/src/components/rjsf-custom-widgets/alt-date-time-widget/AltDateTimeWidget.jsx +++ b/frontend/src/components/rjsf-custom-widgets/alt-date-time-widget/AltDateTimeWidget.jsx @@ -4,7 +4,15 @@ import PropTypes from "prop-types"; import { RjsfWidgetLayout } from "../../../layouts/rjsf-widget-layout/RjsfWidgetLayout.jsx"; -const AltDateTimeWidget = ({ id, value, onChange, label, required }) => { +const AltDateTimeWidget = ({ + id, + value, + onChange, + label, + schema, + required, +}) => { + const description = schema?.description || ""; const handleDateChange = (date) => { onChange(date?.toISOString()); }; @@ -17,7 +25,11 @@ const AltDateTimeWidget = ({ id, value, onChange, label, required }) => { }; return ( - + { +const AltDateWidget = ({ id, value, onChange, label, schema, required }) => { + const description = schema?.description || ""; + const handleDateChange = (date) => { onChange(date?.toISOString()); }; return ( - + { +const CheckboxWidget = ({ id, value, onChange, label, schema, required }) => { const description = schema?.description || ""; const handleCheckboxChange = (event) => { onChange(event.target.checked); @@ -11,7 +11,10 @@ const CheckboxWidget = ({ id, value, onChange, label, schema }) => { return ( - {label} + + {required && * } + {label} + {description?.length > 0 && ( { +const CheckboxesWidget = ({ + id, + options, + value, + onChange, + label, + schema, + required, +}) => { + const description = schema?.description || ""; const handleCheckboxChange = (optionValue) => { const newValue = [...(value || [])]; const index = newValue.indexOf(optionValue); @@ -16,7 +25,11 @@ const CheckboxesWidget = ({ id, options, value, onChange, label }) => { }; return ( - + {options.map((option) => ( { +const ColorWidget = ({ id, value, onChange, schema, label, required }) => { + const description = schema?.description || ""; + const handleColorChange = (event) => { onChange(event.target.value); }; return ( - + ); @@ -19,7 +25,9 @@ ColorWidget.propTypes = { id: PropTypes.string.isRequired, value: PropTypes.string, onChange: PropTypes.func.isRequired, + schema: PropTypes.object.isRequired, label: PropTypes.string.isRequired, + required: PropTypes.bool, }; export { ColorWidget }; diff --git a/frontend/src/components/rjsf-custom-widgets/date-time-widget/DateTimeWidget.jsx b/frontend/src/components/rjsf-custom-widgets/date-time-widget/DateTimeWidget.jsx index 218988cf1..a3b2df307 100644 --- a/frontend/src/components/rjsf-custom-widgets/date-time-widget/DateTimeWidget.jsx +++ b/frontend/src/components/rjsf-custom-widgets/date-time-widget/DateTimeWidget.jsx @@ -4,13 +4,18 @@ import PropTypes from "prop-types"; import { RjsfWidgetLayout } from "../../../layouts/rjsf-widget-layout/RjsfWidgetLayout.jsx"; -const DateTimeWidget = ({ id, value, onChange, label, required }) => { +const DateTimeWidget = ({ id, value, onChange, label, schema, required }) => { + const description = schema?.description || ""; const handleDateTimeChange = (dateTime) => { onChange(dateTime?.toISOString()); }; return ( - + { +const DateWidget = ({ id, value, onChange, label, schema, required }) => { + const description = schema?.description || ""; const handleDateChange = (date) => { onChange(date?.toISOString()); }; return ( - + { +const EmailWidget = ({ id, value, onChange, label, schema, required }) => { + const description = schema?.description || ""; const handleEmailChange = (event) => { onChange(event.target.value); }; return ( - + ); @@ -20,6 +25,7 @@ EmailWidget.propTypes = { value: PropTypes.string, onChange: PropTypes.func.isRequired, label: PropTypes.string.isRequired, + schema: PropTypes.object.isRequired, required: PropTypes.bool, }; diff --git a/frontend/src/components/rjsf-custom-widgets/file-widget/FileWidget.jsx b/frontend/src/components/rjsf-custom-widgets/file-widget/FileWidget.jsx index 4ba89d1a6..4a43ed121 100644 --- a/frontend/src/components/rjsf-custom-widgets/file-widget/FileWidget.jsx +++ b/frontend/src/components/rjsf-custom-widgets/file-widget/FileWidget.jsx @@ -4,7 +4,8 @@ import PropTypes from "prop-types"; import { RjsfWidgetLayout } from "../../../layouts/rjsf-widget-layout/RjsfWidgetLayout.jsx"; -const FileWidget = ({ id, onChange, label, required }) => { +const FileWidget = ({ id, onChange, label, schema, required }) => { + const description = schema?.description || ""; const handleFileChange = (info) => { if (info.file.status === "done") { const fileUrl = info.file.response.url; // Assuming the response contains the uploaded file URL @@ -13,7 +14,11 @@ const FileWidget = ({ id, onChange, label, required }) => { }; return ( - + @@ -25,6 +30,7 @@ FileWidget.propTypes = { id: PropTypes.string.isRequired, onChange: PropTypes.func.isRequired, label: PropTypes.string.isRequired, + schema: PropTypes.object.isRequired, required: PropTypes.bool, }; diff --git a/frontend/src/components/rjsf-custom-widgets/select-widget/SelectWidget.jsx b/frontend/src/components/rjsf-custom-widgets/select-widget/SelectWidget.jsx index acc2be756..4820b96c6 100644 --- a/frontend/src/components/rjsf-custom-widgets/select-widget/SelectWidget.jsx +++ b/frontend/src/components/rjsf-custom-widgets/select-widget/SelectWidget.jsx @@ -19,21 +19,28 @@ const SelectWidget = (props) => { > {label} - - {description?.length && ( - - )} +
+ + {description?.length && ( + + )} +
); diff --git a/frontend/src/components/rjsf-custom-widgets/time-widget/TimeWidget.jsx b/frontend/src/components/rjsf-custom-widgets/time-widget/TimeWidget.jsx index f26ced515..bad749ffa 100644 --- a/frontend/src/components/rjsf-custom-widgets/time-widget/TimeWidget.jsx +++ b/frontend/src/components/rjsf-custom-widgets/time-widget/TimeWidget.jsx @@ -4,13 +4,18 @@ import PropTypes from "prop-types"; import { RjsfWidgetLayout } from "../../../layouts/rjsf-widget-layout/RjsfWidgetLayout.jsx"; -const TimeWidget = ({ id, value, onChange, label, required }) => { +const TimeWidget = ({ id, value, onChange, label, schema, required }) => { + const description = schema?.description || ""; const handleTimeChange = (time) => { onChange(time?.toISOString()); }; return ( - + { +const UpDownWidget = ({ id, value, onChange, label, schema, required }) => { + const description = schema?.description || ""; const handleNumberChange = (numberValue) => { onChange(numberValue); }; return ( - + ); @@ -20,6 +25,7 @@ UpDownWidget.propTypes = { value: PropTypes.number, onChange: PropTypes.func.isRequired, label: PropTypes.string.isRequired, + schema: PropTypes.object.isRequired, required: PropTypes.bool, }; diff --git a/frontend/src/components/rjsf-custom-widgets/url-widget/URLWidget.jsx b/frontend/src/components/rjsf-custom-widgets/url-widget/URLWidget.jsx index c2aba010e..ab1425713 100644 --- a/frontend/src/components/rjsf-custom-widgets/url-widget/URLWidget.jsx +++ b/frontend/src/components/rjsf-custom-widgets/url-widget/URLWidget.jsx @@ -1,20 +1,22 @@ -import { Form, Input } from "antd"; +import { Input } from "antd"; import PropTypes from "prop-types"; +import { RjsfWidgetLayout } from "../../../layouts/rjsf-widget-layout/RjsfWidgetLayout"; -const URLWidget = ({ id, value, onChange, rawErrors }) => { +const URLWidget = (props) => { + const { id, value, onChange, label, schema, required } = props; + const description = schema?.description || ""; const handleURLChange = (event) => { onChange(event.target.value); }; - const hasError = rawErrors && rawErrors.length > 0; - return ( - - + ); }; @@ -22,7 +24,9 @@ URLWidget.propTypes = { id: PropTypes.string.isRequired, value: PropTypes.string, onChange: PropTypes.func.isRequired, - rawErrors: PropTypes.array, + label: PropTypes.string.isRequired, + schema: PropTypes.object.isRequired, + required: PropTypes.bool, }; export { URLWidget }; From 9013814921becf770bc5f935942144fde4aef348 Mon Sep 17 00:00:00 2001 From: Tahier Hussain Date: Wed, 4 Dec 2024 18:12:27 +0530 Subject: [PATCH 08/18] Add form-level description and reduce spacing between checkbox fields and their helper text --- .../checkbox-widget/CheckboxWidget.jsx | 2 +- .../rjsf-form-layout/RjsfFormLayout.jsx | 59 ++++++++++++------- 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/frontend/src/components/rjsf-custom-widgets/checkbox-widget/CheckboxWidget.jsx b/frontend/src/components/rjsf-custom-widgets/checkbox-widget/CheckboxWidget.jsx index d4f42f0ca..4ea6ba0b1 100644 --- a/frontend/src/components/rjsf-custom-widgets/checkbox-widget/CheckboxWidget.jsx +++ b/frontend/src/components/rjsf-custom-widgets/checkbox-widget/CheckboxWidget.jsx @@ -9,7 +9,7 @@ const CheckboxWidget = ({ id, value, onChange, label, schema, required }) => { }; return ( - + {required && * } diff --git a/frontend/src/layouts/rjsf-form-layout/RjsfFormLayout.jsx b/frontend/src/layouts/rjsf-form-layout/RjsfFormLayout.jsx index ea5cd90ff..9e5720c0c 100644 --- a/frontend/src/layouts/rjsf-form-layout/RjsfFormLayout.jsx +++ b/frontend/src/layouts/rjsf-form-layout/RjsfFormLayout.jsx @@ -22,6 +22,9 @@ import { PasswordWidget } from "../../components/rjsf-custom-widgets/password-wi import { UpDownWidget } from "../../components/rjsf-custom-widgets/up-down-widget/UpDownWidget.jsx"; import { CustomFieldTemplate } from "./CustomFieldTemplate.jsx"; import "./RjsfFormLayout.css"; +import { useEffect, useState } from "react"; +import { Alert, Space } from "antd"; +import CustomMarkdown from "../../components/helpers/custom-markdown/CustomMarkdown.jsx"; function RjsfFormLayout({ children, @@ -33,8 +36,14 @@ function RjsfFormLayout({ validateAndSubmit, isStateUpdateRequired, }) { - schema.title = ""; - schema.description = ""; + const [description, setDescription] = useState(""); + + useEffect(() => { + setDescription(schema?.description || ""); + schema.title = ""; + schema.description = ""; + }, []); + const widgets = { AltDateTimeWidget, AltDateWidget, @@ -112,25 +121,33 @@ function RjsfFormLayout({ {isLoading ? ( ) : ( -
{}} - onSubmit={(e) => validateAndSubmit(e.formData)} - showErrorList={false} - onChange={handleChange} - templates={{ - FieldTemplate: CustomFieldTemplate, - }} - > - {children} -
+ + {description && ( + } + type="info" + /> + )} +
{}} + onSubmit={(e) => validateAndSubmit(e.formData)} + showErrorList={false} + onChange={handleChange} + templates={{ + FieldTemplate: CustomFieldTemplate, + }} + > + {children} +
+
)} ); From c38be7bdfac17d92ed15bdc1aee17b9c710fe5db Mon Sep 17 00:00:00 2001 From: Tahier Hussain Date: Wed, 4 Dec 2024 18:50:10 +0530 Subject: [PATCH 09/18] Minor bug fixes in rendering RJSF form --- .../rjsf-custom-widgets/select-widget/SelectWidget.jsx | 2 +- frontend/src/layouts/rjsf-form-layout/RjsfFormLayout.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/rjsf-custom-widgets/select-widget/SelectWidget.jsx b/frontend/src/components/rjsf-custom-widgets/select-widget/SelectWidget.jsx index 4820b96c6..50f2ab26f 100644 --- a/frontend/src/components/rjsf-custom-widgets/select-widget/SelectWidget.jsx +++ b/frontend/src/components/rjsf-custom-widgets/select-widget/SelectWidget.jsx @@ -33,7 +33,7 @@ const SelectWidget = (props) => { ))} - {description?.length && ( + {description?.length > 0 && ( Date: Wed, 4 Dec 2024 19:30:12 +0530 Subject: [PATCH 10/18] Optimize the RjsfLayout component --- .../select-widget/SelectWidget.jsx | 7 +- .../rjsf-form-layout/RjsfFormLayout.jsx | 125 +++++++++--------- 2 files changed, 68 insertions(+), 64 deletions(-) diff --git a/frontend/src/components/rjsf-custom-widgets/select-widget/SelectWidget.jsx b/frontend/src/components/rjsf-custom-widgets/select-widget/SelectWidget.jsx index 50f2ab26f..49af88ee7 100644 --- a/frontend/src/components/rjsf-custom-widgets/select-widget/SelectWidget.jsx +++ b/frontend/src/components/rjsf-custom-widgets/select-widget/SelectWidget.jsx @@ -13,11 +13,8 @@ const SelectWidget = (props) => { const hasError = rawErrors && rawErrors.length > 0; return ( - - + + {label}