diff --git a/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/Documents/OverviewMarkdown.jsx b/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/Documents/OverviewMarkdown.jsx new file mode 100644 index 00000000000..0d1c14d3329 --- /dev/null +++ b/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/Documents/OverviewMarkdown.jsx @@ -0,0 +1,112 @@ +/* eslint-disable react/no-children-prop */ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { + useState, useEffect, useContext, Suspense, lazy, +} from 'react'; +import { styled } from '@mui/material/styles'; +import PropTypes from 'prop-types'; +import { injectIntl } from 'react-intl'; +import API from 'AppData/api'; +import Settings from 'Settings'; +import remarkGfm from 'remark-gfm'; +import CircularProgress from '@mui/material/CircularProgress'; +import { vscDarkPlus, vs } from 'react-syntax-highlighter/dist/esm/styles/prism'; +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { ApiContext } from '../ApiContext'; + +const PREFIX = 'OverviewMarkdown'; + +const classes = { + root: `${PREFIX}-root`, +}; + +const Root = styled('div')(({ theme }) => ({ + [`& .${classes.root}`]: { + paddingBottom: theme.spacing(2), + paddingTop: theme.spacing(2), + }, +})); + +const ReactMarkdown = lazy(() => import('react-markdown' /* webpackChunkName: "MDReactMarkdown" */)); + +function OverviewMarkdown({ apiId }) { + const { api } = useContext(ApiContext); + const [content, setContent] = useState(''); + const restAPI = new API(); + const { skipHtml, syntaxHighlighterProps = {}, syntaxHighlighterDarkTheme } = Settings.app.markdown; + + useEffect(() => { + restAPI.getMarkdownContentOfAPI(apiId) + .then(({ text }) => { + const updatedText = Object.entries(api).reduce((acc, [fieldName, value]) => { + return acc.replace(new RegExp(`___${fieldName}___`, 'g'), value); + }, text); + setContent(updatedText); + }) + .catch((error) => { + if (process.env.NODE_ENV !== 'production') { + console.error(error); + } + }); + }, [apiId]); + + return ( + +
+ }> + {children} + ); + } + + const syntaxProps = { + style: syntaxHighlighterDarkTheme ? vscDarkPlus : vs, + language: match[1], + PreTag: 'div', + ...propsInner, + ...syntaxHighlighterProps, + }; + return {codeContent}; + }, + }} + > + {content} + + +
+
+ ); +} + +OverviewMarkdown.propTypes = { + apiId: PropTypes.string.isRequired, +}; + +export default injectIntl((OverviewMarkdown)); diff --git a/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/Overview.jsx b/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/Overview.jsx index 651ba800f83..ec9f8623743 100644 --- a/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/Overview.jsx +++ b/portals/devportal/src/main/webapp/source/src/app/components/Apis/Details/Overview.jsx @@ -49,7 +49,7 @@ import InlineMessage from 'AppComponents/Shared/InlineMessage'; import Progress from 'AppComponents/Shared/Progress'; import API from 'AppData/api'; import CONSTANTS from 'AppData/Constants'; -import View from 'AppComponents/Apis/Details/Documents/View'; +import OverviewMarkdown from 'AppComponents/Apis/Details/Documents/OverviewMarkdown'; import Settings from 'Settings'; import SolaceEndpoints from './SolaceEndpoints'; import Environments from './Environments'; @@ -192,7 +192,6 @@ function Overview() { const [isLoading, setIsLoading] = useState(false); const [notFound, setNotFound] = useState(false); const [allDocuments, setAllDocuments] = useState(null); - const [overviewDocOverride, setOverviewDocOverride] = useState(null); const [swaggerDescription, setSwaggerDescription] = useState(null); const [allPolicies, setAllPolicies] = useState(null); const [rating, setRating] = useState({ @@ -294,11 +293,6 @@ function Overview() { const restApi = new API(); return restApi.getDocumentsByAPIId(api.id) .then((response) => { - const overviewDoc = response.body.list.filter((item) => item.otherTypeName === '_overview'); - if (overviewDoc.length > 0) { - // We can override the UI with this content - setOverviewDocOverride(overviewDoc[0]); // Only one doc we can render - } setAllDocuments(response.body.list); }) .catch((error) => { @@ -431,11 +425,11 @@ function Overview() { /> ); } - if (overviewDocOverride) { + if (api.isMarkdownOverview) { return ( - + ); diff --git a/portals/devportal/src/main/webapp/source/src/app/data/api.jsx b/portals/devportal/src/main/webapp/source/src/app/data/api.jsx index adfd2e391fc..328e548742d 100644 --- a/portals/devportal/src/main/webapp/source/src/app/data/api.jsx +++ b/portals/devportal/src/main/webapp/source/src/app/data/api.jsx @@ -84,6 +84,19 @@ export default class API extends Resource { return promised_getDocContent; } + /* + * Get the markdown overview content of an API + */ + getMarkdownContentOfAPI(api_id) { + const promiseGetMarkdDownContent = this.client.then((client) => { + const payload = { + apiId: api_id, + }; + return client.apis.APIs.getMarkdownContentOfAPI(payload); + }); + return promiseGetMarkdDownContent; + } + /** * Get the Documents of an API * @param id {String} UUID of the API in which the documents needed