import React, { useEffect, useLayoutEffect, useRef, useState, useContext, memo, } from "react"
import { toast } from 'react-toastify';
import Markdown from 'react-markdown'
import {getTheme} from '../theme.jsx';
import ReactJson from "react-json-view-ssr";
import { isMobile } from "react-device-detect";
import { BrowserView, MobileView } from "react-device-detect";
import { useParams, useNavigate, Link, useLocation } from "react-router-dom";
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
import remarkGfm from 'remark-gfm'
import { Context } from "../context/ContextApi.jsx";
import {
Grid,
TextField,
IconButton,
Tooltip,
Divider,
Button,
Menu,
MenuItem,
Typography,
Paper,
List,
Collapse,
ListItemButton,
ListItemText,
Dialog,
DialogTitle,
Box,
DialogContent,
InputAdornment,
CircularProgress
} from "@mui/material";
import {
Link as LinkIcon,
Edit as EditIcon,
KeyboardArrowRight as KeyboardArrowRightIcon,
ExpandMore as ExpandMoreIcon,
FileCopy as FileCopyIcon,
ChevronLeft,
ChevronRight,
Search as SearchIcon,
Close as CloseIcon
} from "@mui/icons-material";
import { fontGrid } from "@mui/material/styles/cssUtils.js";
import SearchBox from "../components/SearchData.jsx";
const Body = {
//maxWidth: 1000,
//minWidth: 768,
maxWidth: "100%",
minWidth: "100%",
display: "flex",
height: "100%",
color: "white",
position: "relative",
paddingTop: 40,
//textAlign: "center",
};
const dividerColor = "rgb(225, 228, 232)";
const hrefStyle = {
color: "rgba(255, 255, 255, 0.8)",
textDecoration: "none",
marginRight: window.location.pathname.includes("/articles/") ? "0.8em" : undefined,
};
const hrefStyleToc2 = {
color: "rgba(255, 255, 255, 0.6)",
textDecoration: "none",
fontSize: "14px",
fontWeight: 400,
padding: "4px 0",
paddingLeft: "12px",
paddingRight: "12px",
lineHeight: "20px",
};
const hrefStyle2 = {
color: "#f86a3e",
textDecoration: "none",
};
const innerHrefStyle = {
color: "rgba(255, 255, 255, 0.75)",
textDecoration: "none",
};
export const CopyToClipboard = (props) => {
const { text, style, onCopy } = props;
const parsedstyle = style !== undefined ? style : {
position: "absolute",
right: 0,
top: -10,
}
return (
{
navigator.clipboard.writeText(text);
toast("Copied to clipboard")
}}
>
)
}
export const Paragraph = (props) => {
const element = React.createElement(
`p`,
{},
props.children,
)
if (props.children[0] != undefined) {
if(typeof props.children[0] === "string") {
if (props.children[0].includes('.mp4')) {
return (
)
}
}
}
return (
{element}
)
}
export const OuterLink = (props) => {
if (props.href.includes("http") || props.href.includes("mailto")) {
return (
{props.children}
);
}
return (
{props.children}
);
}
export const Img = (props) => {
// Find parent container and check width
const isArticlePage = window.location.pathname.includes("/articles/")
const isFormPage = window.location.pathname.includes("/forms/")
var height = "auto"
var width = isArticlePage ? 1000 : isFormPage ? 400: 750
const { themeMode } = useContext(Context)
const theme = getTheme(themeMode)
const docsImageStyle = {
border: isFormPage ? null : "1px solid rgba(255,255,255,0.3)",
borderRadius: theme.palette?.borderRadius,
width: width,
maxWidth: width,
margin: "auto",
marginTop: 10,
marginBottom: 10
}
const articleImageStyle = {
borderRadius: theme.palette?.borderRadius,
minWidth: 350,
maxWidth: "100%",
textAlign: "center",
margin: "auto",
marginTop: 20,
marginBottom: 20,
}
if (props.height !== undefined && props.height !== null) {
height = props.height
}
if (props.width !== undefined && props.width !== null) {
width = props.width
}
return(
)
}
export const CodeHandler = (props) => {
const propvalue = props.value !== undefined && props.value !== null ? props.value : props.children !== undefined && props.children !== null && props.children.length > 0 ? props.children[0] : ""
const validate = validateJson(propvalue)
const {themeMode } = useContext(Context)
const theme = getTheme(themeMode)
var newprop = propvalue
if (validate.valid === false) {
// Check if https://shuffler.io in the url
// if so, then we change it for the current url
if (propvalue.includes("https://shuffler.io/api")) {
newprop = propvalue.replace("https://shuffler.io/api", window.location.origin+"/api")
const foundurl = localStorage.getItem("globalUrl")
if (foundurl !== undefined && foundurl !== null && foundurl !== "") {
newprop = propvalue.replace("https://shuffler.io/api", foundurl+"/api")
}
}
}
// Need to check if it's singletick or multi
if (props.inline === true) {
// Show it inline
return (
{newprop}
)
}
return (
{validate.valid === true ?
:
{newprop}
}
)
}
const Docs = (defaultprops) => {
const { globalUrl, selectedDoc, serverside, serverMobile, isLoggedIn, isLoaded, userdata } = defaultprops;
const { searchBarModalOpen, setSearchBarModalOpen, isDocSearchModalOpen, setIsDocSearchModalOpen, leftSideBarOpenByClick } = useContext(Context);
let navigate = useNavigate();
const location = useLocation();
const pathname = location.pathname
// Quickfix for react router 5 -> 6
const params = useParams();
//var props = JSON.parse(JSON.stringify(defaultprops))
var props = Object.assign({ selected: false }, defaultprops);
props.match = {}
props.match.params = params
//console.log("PARAMS: ", params)
const { themeMode } = useContext(Context)
const theme = getTheme(themeMode)
const [mobile, setMobile] = useState(serverMobile === true || isMobile === true ? true : false);
const [data, setData] = useState("");
const [firstrequest, setFirstrequest] = useState(true);
const [list, setList] = useState([]);
const [lastHeading, setLastHeading] = useState(false);
const [activeId, setActiveId] = useState()
const [isopen, setOpen] = useState(-1);
const [hover, setHover] = useState(false);
const [, setListLoaded] = useState(false);
const [anchorEl, setAnchorEl] = React.useState(null);
const [anchorElToc, setAnchorElToc] = React.useState(null);
const [headingSet, setHeadingSet] = React.useState(false);
const [selectedMeta, setSelectedMeta] = React.useState({
link: "hello",
read_time: 2,
});
const [tocLines, setTocLines] = React.useState([]);
const [baseUrl, setBaseUrl] = React.useState(
serverside === true ? "" : window.location.href
);
const [hashRendered, setHashRendered] = React.useState(false)
const [sidebarOpen, setSidebarOpen] = useState(false);
const [activeSubItem, setActiveSubItem] = useState(false);
const headingElementsRef = useRef({})
const [hasRedirected, setHasRedirected] = useState(false)
var isArticlePage = window.location.pathname.includes("/articles/") || window.location.pathname === "/articles" ? true : false;
const searchFieldRef = useRef(null);
const handleDocRedirectForPartners = () => {
if (hasRedirected) return;
if (
userdata &&
userdata?.org_status?.includes("integration_partner") &&
userdata?.active_org?.branding?.documentation_link?.length > 0
) {
const docLink = userdata?.active_org?.branding?.documentation_link;
if (docLink && docLink !== "") {
setHasRedirected(true);
if (docLink.startsWith('http')) {
window.location.replace(docLink);
} else {
navigate(docLink);
}
}
}
};
useEffect(() => {
if (isLoggedIn && isLoaded) {
handleDocRedirectForPartners()
}
}, [isLoggedIn, isLoaded]);
useEffect(() => {
fetchDocList();
setSidebarOpen(false);
if (props.match.params === undefined || props.match.params === null) {
return
}
const propkey = props.match.params.key
if (propkey === undefined || propkey === null) {
return
}
console.log("PROPKEY: ", propkey)
if (location.pathname.includes("/docs/")) {
if (propkey === "cookie_policy" || propkey === "compliance" || propkey === "privacy_policy" || propkey === "terms_of_service") {
navigate(`/legal/${propkey}`)
}
if (propkey === "app_creation") {
navigate('/docs/apps#app-creation-introduction')
}
}
}, [location]);
useEffect(() => {
return () => {
setIsDocSearchModalOpen(false);
setSearchBarModalOpen(false);
};
}, []);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
}
const handleClickToc = (event) => {
setAnchorElToc(event.currentTarget);
}
const handleCollapse = (index) => {
setOpen(isopen === index ? -1 : index)
}
const handleMouseOver = () => {
setHover(!hover);
}
const handleClose = () => {
setAnchorEl(null)
}
const handleCloseToc = () => {
setAnchorElToc(null)
}
const intersectionObserver = () => {
const callback = (headings) => {
headingElementsRef.current = headings.reduce((map, headingElement) => {
if (map === undefined) {
return {}
}
if (headingElement === undefined || headingElement.target === undefined || headingElement.target.id === undefined || headingElement.target.id === null || headingElement.target.id === "") {
return map
}
if (headingElement.target.id != undefined || headingElement.target.id != "") {
map[headingElement.target.id] = headingElement
return map
}
}, headingElementsRef.current)
const visibleHeadings = []
const keys = Object.keys(headingElementsRef.current)
for (var i = 0; i < keys.length; i++) {
const headingElement = headingElementsRef.current[keys[i]]
if (headingElement.isIntersecting) {
visibleHeadings.push(headingElement)
}
}
if (visibleHeadings.length > 0) {
const tocs = document.getElementsByClassName('toc')
for (const t of tocs) {
const currentPoint = t.getElementsByTagName('a')[0]
currentPoint.style.color = "white";
if (`#${visibleHeadings[0].target.id}`
=== currentPoint.hash) {
currentPoint.style.color = "#f86a3e";
}
}
}
}
const observer = new IntersectionObserver(callback, {
threshold: [1]
})
const headingElements = Array.from(document.querySelectorAll("h2"))
if (headingElements.length != 0) {
for (var i = 0; i < headingElements.length; i++) {
var element = headingElements[i]
observer.observe(element)
}
return () => observer.disconnect()
}
}
if (lastHeading) {
setTimeout(() => {
intersectionObserver()
}, 100)
}
const scrollToHash = () => {
var hash = window.location.hash.replace("#", "").replaceAll("_", "-");
if (hash.includes('?')) {
hash = hash.split('?')[0]
}
if (hash) {
const element = document.getElementById(hash.toLowerCase())
if (element) {
element.scrollIntoView({
behavior: "instant",
})
}
}
}
const extractHeadings = content => {
const headings = [];
const lines = content.split('\n');
let inCodeBlock = false;
for (const line of lines) {
if (line.startsWith('```')) {
inCodeBlock = !inCodeBlock;
}
if (!inCodeBlock) {
const headingMatch = line.match(/^(#{2,3})\s+(.+)$/);
if (headingMatch) {
const level = headingMatch[1].length;
const title = headingMatch[2].trim();
const id = title.toLowerCase().replace(/\s+/g, '-');
if (level === 2) {
headings.push({ id, title, items: [] });
} else if (level === 3 && headings.length) {
headings[headings.length - 1].items.push({ id, title });
}
}
}
}
return headings;
};
const modalView = (
{
setSearchBarModalOpen(false);
if (searchFieldRef.current) {
searchFieldRef.current.blur();
}
}}
PaperProps={{
sx: {
color: theme.palette.DialogStyle.color,
minWidth: "750px",
minHeight: "180px",
maxHeight: "85vh",
borderRadius: theme.palette.DialogStyle.borderRadius,
border: "1px solid var(--Container-Stroke, #494949)",
background: theme.palette.DialogStyle.backgroundColor,
boxShadow: "0px 16px 24px 8px rgba(0, 0, 0, 0.25)",
position: "fixed",
top: "70px",
left: "50%",
transform: "translateX(-50%)",
'& .MuiDialogContent-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
'& .MuiDialogTitle-root': {
backgroundColor: theme?.palette?.DialogStyle?.backgroundColor,
},
},
}}
sx={{
zIndex: 50005,
'& .MuiBackdrop-root': {
backgroundColor: 'rgba(0, 0, 0, 0.8)',
},
}}
>
Search for Documentation
setSearchBarModalOpen(false)}
sx={{
color: 'white',
'&:hover': {
backgroundColor: 'rgba(255, 255, 255, 0.1)'
}
}}
>
);
// extract TOC from actual markdown
const tocvalue = (markdown) => {
const items = [];
let currentMainItem = null;
const lines = markdown.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line.startsWith('* [') && !line.startsWith(" ", 5)) {
const matches = line.match(/^\* \[([^)]+)\]\(#([^)]+)\)/);
if (matches) {
currentMainItem = {
id: matches[2],
title: matches[1],
items: [],
};
items.push(currentMainItem);
}
} else if (line.startsWith(' * [')) {
if (currentMainItem) {
const matches = line.match(/^ \* \[([^)]+)\]\(#([^)]+)\)/);
if (matches) {
currentMainItem.items.push({
id: matches[2],
title: matches[1],
});
}
}
} else if (line.startsWith('##')) {
continue
}
}
return items
}
const SidebarPaperStyle = {
backgroundColor: isArticlePage ? "transparent" : theme.palette.backgroundColor,
border: isArticlePage ? "none" : undefined,
borderRadius: isArticlePage ? "none" : undefined,
boxShadow: isArticlePage ? "none" : undefined,
backgroundImage: "none",
width: isArticlePage ? "100%" : undefined,
height: isArticlePage ? "100%" : undefined,
overflowX: "hidden",
position: "relative",
paddingLeft: 15,
paddingRight: 15,
minHeight: "80vh",
};
const noteLabelStyle = {
fontWeight: "bold",
color: "#f86a3e",
display: "block",
marginBottom: "5px",
};
const Blockquote = ({ children }) => {
const textContent = children.map(child =>
child.props && child.props.children ? child.props.children.join('') : child
).join('').trim();
// Maybe some more contents....
const isNote = textContent.startsWith("[!TIP]");
return (
{isNote && Tips: }
{isNote ? textContent.replace("[!TIP]", "").trim() : children}
);
};
const Heading = (props) => {
const [hover, setHover] = useState(false);
var id = props.children[0].toLowerCase().toString()
if (props.level <= 3) {
id = props.children[0].toLowerCase().toString().replaceAll(" ", "-");
}
const element = React.createElement(
`h${props.level}`,
{ id: `${id}` },
props.children,
)
if (serverside !== true && window.location.hash.length > 0 && props.level === 1 && !hashRendered) {
setTimeout(() => {
setHashRendered(true)
scrollToHash()
}, 500)
}
if (serverside !== true && !lastHeading) {
setTimeout(() => {
setLastHeading(true)
}, 500)
}
var extraInfo = "";
if (props.level === 1) {
extraInfo = (
{mobile ? null : (
Edit
)}
{mobile ? null : (
)}
{selectedMeta.read_time} minute
{selectedMeta.read_time === 1 ? "" : "s"} to read
{mobile ||
selectedMeta.contributors === undefined ||
selectedMeta.contributors === null ? (
""
) : (
{selectedMeta.contributors.slice(0, 7).map((data, index) => {
return (
);
})}
)}
);
}
if (extraInfo !== "" && props.level === 1 && props.children !== undefined && props.children !== null && props.children.length > 0) {
if (props.children[0].toLowerCase().includes("privacy") || props.children[0].toLowerCase().includes("terms")) {
extraInfo = ""
}
}
return (
{
setHover(true);
}}
id={id}
style={{
scrollPaddingTop: 20,
}}
>
{props.level !== 1 ? (
) : null}
{element}
{
e.preventDefault(); // Prevent default navigation
document.getElementById(id)?.scrollIntoView({
behavior: 'smooth'
});
const url = `${window.location.origin}${window.location.pathname}#${id}`;
navigator.clipboard.writeText(url);
toast("Link copied to clipboard", {
position: "bottom-center",
autoClose: 2000,
});
}}
style={{
textDecoration: "none", color: "white",
paddingLeft: "0.3em", rotate: "-30deg",
paddingTop: "0.9em", display: props.level === 1 ? "none" : "block",
}}>
{isArticlePage ? (userdata?.support ? extraInfo : "") : extraInfo}
)
}
const SideBar = {
width: "17%",
borderRight: !isArticlePage ? "1px solid rgba(255,255,255,0.3)" : undefined,
minWidth: isArticlePage ? "250px" : "17%",
maxWidth: isArticlePage ? "250px" : "17%",
marginLeft: isArticlePage ? sidebarOpen ? 40 : 50 : undefined,
position: "sticky",
top: isArticlePage ? 120 : 50,
paddingTop: isArticlePage ? "0.65em" : "0.25em",
paddingRight: isArticlePage ? "1em" : undefined,
minHeight: isArticlePage ? "80vh" : "95vh",
maxHeight: isArticlePage ? "80vh" : "95vh",
overflowX: "hidden",
overflowY: "auto",
zIndex: 1000,
backgroundColor: isArticlePage ? "#212121" : undefined,
borderRadius: isArticlePage ? theme.palette?.borderRadius : undefined,
transition: isArticlePage ? "width 0.3s ease, min-width 0.3s ease" : undefined,
};
const sideBarToggleButton = {
position: "absolute",
bottom: sidebarOpen ? 20 : "50%",
right: sidebarOpen ? 30 : 21,
zIndex: 1000,
backgroundColor: "#212121",
border: "1px solid rgba(255,255,255,0.3)",
borderRadius: "50%",
width: 40,
height: 40,
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
transition: "transform 0.3s ease",
"&:hover": {
backgroundColor: "#2c2c2c",
}
};
const collapsedSideBar = {
...SideBar,
width: "40px",
minWidth: "40px",
maxWidth: "40px",
overflow: "hidden",
};
const IndexBar = {
alignSelf: "flex-start",
position: "sticky",
top: 120,
overflowY: "auto",
minHeight: "93vh",
maxHeight: "93vh",
marginTop: 70,
maxWidth: 250,
overflow: "hidden",
}
const fetchDocList = (resetCache = false) => {
var url = `${globalUrl}/api/v1/docs`
if (location.pathname.includes("/legal")) {
url = `${globalUrl}/api/v1/docs?folder=legal&resetCache=${resetCache}`
} else if (location.pathname.includes("/articles")) {
url = `${globalUrl}/api/v1/docs?folder=articles&resetCache=${resetCache}`
}
fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
})
.then((response) => response.json())
.then((responseJson) => {
if (responseJson.success) {
setList(responseJson.list);
} else {
setList(["# Error loading documentation. Please contact us if this persists.",]);
toast("Failed loading documentation. Please reload the window")
}
setListLoaded(true);
})
.catch((error) => { });
};
const fetchDoc = (docId) => {
if (docId === undefined) {
return
}
var url = `${globalUrl}/api/v1/docs/${docId}`
if (location.pathname.includes("/legal")) {
url = `${globalUrl}/api/v1/docs/${docId}?folder=legal`
} else if (location.pathname.includes("/articles")) {
url = `${globalUrl}/api/v1/docs/${docId}?folder=articles`
}
fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
})
.then((response) => response.json())
.then((responseJson) => {
if (responseJson.success === false) {
//toast("Failed loading documentation. Please reload the UI")
}
if (responseJson.success && responseJson.reason !== undefined) {
if(isArticlePage && window.location.pathname.includes("/articles/")) {
if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason.includes("404: Not Found")) {
setData("# Error\nThis page doesn't exist.");
toast("This page doesn't exist. Redirecting to the latest article...", {
position: "bottom-center",
autoClose: 2000,
});
setTimeout(() => {
navigate(`/articles/2.0_release`)
}, 2000)
return
}
}
// Find tags and translate them into ![]() format
const imgRegex = / { });
};
const handleResetCache = () => {
fetchDocList(true);
toast("Cache has been reset");
}
if (firstrequest) {
setFirstrequest(false);
if (!serverside) {
if (window.innerWidth < 768) {
setMobile(true);
}
}
if (selectedDoc !== undefined) {
setData(selectedDoc.reason);
setList(selectedDoc.list);
setListLoaded(true);
} else {
if (!serverside) {
fetchDocList();
//const propkey = props.match.params.key
//if (propkey === undefined) {
// navigate("/docs/about")
// return null
//}
//
if (props.match.params.key === undefined && isArticlePage) {
fetch(`${globalUrl}/api/v1/articles`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
})
.then((response) => response.json())
.then((responseJson) => {
if (responseJson.success && responseJson.list && responseJson.list.length > 0) {
// Navigate to the latest article
if (responseJson?.list[0]?.name) {
navigate(`/articles/${responseJson?.list[0].name}`);
}
}
})
.catch((error) => {
console.error("Error fetching articles:", error);
});
} else {
console.log("DOCID: ", props.match.params.key)
fetchDoc(props.match.params.key)
}
}
}
}
// Handles search-based changes that origin from outside this file
if (serverside !== true && window.location.href !== baseUrl) {
setBaseUrl(window.location.href);
fetchDoc(props.match.params.key);
}
const markdownStyle = {
color: theme.palette.textColor,
overflow: "hidden",
paddingBottom: 100,
margin: "auto",
maxWidth: "100%",
minWidth: "100%",
fontSize: isMobile ? "1.3rem" : "1.1rem",
};
const alertNote = {
padding: "10px",
borderLeft: "5px solid #f86a3e",
backgroundColor: "rgb(26,26,26)",
};
const CustomButton = (props) => {
const { title, icon, link } = props
return (
{
if (link === "" || link === undefined) {
event.preventDefault()
console.log("IN CLICK!")
if (window.drift !== undefined) {
window.drift.api.startInteraction({ interactionId: 340043 })
} else {
console.log("Couldn't find drift in window.drift and not .drift-open-chat with querySelector: ", window.drift)
}
} else {
console.log("Link defined: ", link)
}
}} onMouseOver={() => {
setHover(true)
}}
onMouseOut={() => {
setHover(false);
}}
>
{icon}
{title}
)
}
const DocumentationButton = (props) => {
const { item, link } = props
if (link === undefined || link === null) {
return null
}
return (
{
setHover(true)
}}
onMouseOut={() => {
setHover(false);
}}
>
{item}
)
}
const headerStyle = {
marginTop: 25,
}
const showPartnerLogo = userdata?.org_status?.includes("integration_partner") && userdata?.active_org?.image !== undefined && userdata?.active_org?.image !== null && userdata?.active_org?.image.length > 0
const mainpageInfo =
Documentation
{showPartnerLogo === true ? null :
/>
link="https://discord.gg/B2CBzUm" />
}
Tutorial
Dive in. Hands-on is the best approach to see how Shuffle can transform your security operations. Our set of tutorials and videos teach you how to build your skills. Check out the getting started section to give it a go!
Why Shuffle?
Security first. We incentivize trying before buying, and give you the full set of tools you need to automate your operations. What's more is we also help you find usecases that fit your unique needs. Accessibility is key, and we intend to help every SOC globally use and share their usecases.
Get help
Our promise is to make it easier and easier to automate your operations. In some cases however, it may be good with a helping hand. That's where Shuffle's consultancy and support services come in handy. We help you build and automate your operational processes to a level you haven't seen before with the help of our usecases .
APIs
Learn. We're all about learning, and are continuously creating documentation and video tutorials to better understand how to get started. APIs are an extremely important part of how the internet works today, and our goal is helping every security professional learn about them.
Workflow building
Build. Creating workflows has never been easier. Jump into things with our getting Started section and build to your hearts content. Workflows make it all come together, with an easy to use area.
Managing Shuffle
Organize. Whether an organization of 1000 or 1, management tools are necessary. In Shuffle we offer full user management, MFA and single-signon options, multi-tenancy and a lot more - for free!
const markdownComponents = {
img: Img,
code: CodeHandler,
h1: Heading,
h2: Heading,
h3: Heading,
h4: Heading,
h5: Heading,
h6: Heading,
a: OuterLink,
p: Paragraph,
blockquote: Blockquote,
}
const activeHrefStyleToc2 = {
...hrefStyleToc2,
color: "#f86a3e",
fontFamily: theme.typography.fontFamily,
};
const activeListItemStyle = {
backgroundColor: "rgba(248, 106, 62, 0.08)", // Slight orange tint
marginRight: window.location.pathname.includes("/articles/") ? "0.8em" : undefined,
borderLeft: "3px solid #f86a3e",
paddingLeft: "13px", // Compensate for the border
};
const toggleSidebar = () => {
setSidebarOpen(!sidebarOpen);
};
// PostDataBrowser Section
const postDataBrowser =
list === undefined || list === null ? null : (
{modalView}
{
!isArticlePage && (
{
setIsDocSearchModalOpen(true)
setSearchBarModalOpen(true)
}}
inputRef={searchFieldRef}
InputProps={{
endAdornment: (
),
style: {
backgroundColor: theme.palette.platformColor,
borderRadius: 4,
fontSize: 18,
color: theme.palette.textColor,
},
}}
style={{
marginTop: 10,
marginLeft: 10,
borderRadius: 4,
width: !isLoggedIn ? "14%" : !leftSideBarOpenByClick ? "12%" : "11%",
maxWidth: "285px",
position: "fixed",
zIndex: 1100,
}}
inputProps={{
readOnly: true,
}}
/>
)
}
{list.map((data, index) => {
const item = data.name;
if (item === undefined) {
return null;
}
var path = "/docs/" + item;
if (location.pathname.includes("/legal")) {
path = "/legal/" + item
} else if (location.pathname.includes("/articles")) {
path = "/articles/" + item
}
const newname =
item.charAt(0).toUpperCase() +
item.substring(1).split("_").join(" ").split("-").join(" ");
const itemMatching = props.match.params.key === undefined ? false :
props.match.params.key.toLowerCase() === item.toLowerCase();
return (
{newname}
}
/>
);
})}
{
isArticlePage && (
{sidebarOpen ? : }
)
}
{props.match.params.key === undefined ?
mainpageInfo
:
{data}
}
{userdata?.support && isArticlePage && (
Reset Cache
)}
{tocLines.length > 0 ?
(
Table Of Content
) : null}
{tocLines.map((data, index) => {
return (
{
handleCollapse(index)
setActiveId(data.id)
}}
>
{data.title}
{data.items.length > 0 ? (
<>{isopen === index ? : }>
) : null}
{
data.items.length > 0 &&
data.items !== null &&
data.items !== undefined ? (
{data.items.map((d, i) => {
return (
{
// e.preventDefault()
setActiveSubItem(d.id)
}}
>
{d.title}
)
})}
) : null
}
)
})}
);
const mobileStyle = {
color: "white",
marginLeft: 25,
marginRight: 25,
paddingBottom: 50,
backgroundColor: "inherit",
display: "flex",
flexDirection: "column",
};
const postDataMobile =
list === undefined || list === null ? null : (
More {isArticlePage ? "articles" : "docs"}
Table Of Contents
{props.match.params.key === undefined ?
mainpageInfo
:
{data}
}
More {isArticlePage ? "articles" : "docs"}
);
// Padding and zIndex etc set because of footer in cloud.
const loadedCheck = (
);
return {loadedCheck}
;
};
export default Docs;
const DocsContent = memo(({postDataBrowser, postDataMobile}) => {
const { themeMode } = useContext(Context);
const theme = getTheme(themeMode);
return(
{postDataBrowser}
{postDataMobile}
)})
const DocsWrapper = memo(({isLoggedIn, isLoaded, children, userdata })=>{
const { leftSideBarOpenByClick, windowWidth } = useContext(Context);
useEffect(() => {
if (isLoaded && isLoggedIn && userdata?.org_status?.includes("integration_partner") && userdata?.active_org?.branding.documentation_link?.length > 0) {
window.location.href = userdata?.active_org?.branding.documentation_link;
}
}, [isLoaded, isLoggedIn, userdata]);
if (isLoaded && isLoggedIn && userdata?.org_status?.includes("integration_partner") && userdata?.active_org?.branding.documentation_link?.length > 0) {
return ;
}
return (
{children}
)
})