diff --git a/navbar.js b/navbar.js index b26f4f92d..56ac30c27 100644 --- a/navbar.js +++ b/navbar.js @@ -70,7 +70,7 @@ module.exports = { { position: 'left', label: 'Blog', - to: 'https://medium.com/sailpointengineering', + to: '/blog', }, { position: 'left', diff --git a/package-lock.json b/package-lock.json index 1ab33f327..481d9c765 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,10 @@ "docusaurus-theme-openapi-docs": "^1.5.1", "prism-react-renderer": "^1.3.1", "react": "^17.0.2", - "react-dom": "^17.0.2" + "react-dom": "^17.0.2", + "react-markdown": "^8.0.7", + "react-spinners": "^0.13.8", + "react-tabs": "^4.3.0" }, "devDependencies": { "@docusaurus/core": "2.2.0", @@ -11024,8 +11027,9 @@ "license": "MIT" }, "node_modules/react-markdown": { - "version": "8.0.5", - "license": "MIT", + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-8.0.7.tgz", + "integrity": "sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==", "dependencies": { "@types/hast": "^2.0.0", "@types/prop-types": "^15.0.0", @@ -11302,6 +11306,27 @@ "react": ">=15" } }, + "node_modules/react-spinners": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.13.8.tgz", + "integrity": "sha512-3e+k56lUkPj0vb5NDXPVFAOkPC//XyhKPJjvcGjyMNPWsBKpplfeyialP74G7H7+It7KzhtET+MvGqbKgAqpZA==", + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-tabs": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/react-tabs/-/react-tabs-4.3.0.tgz", + "integrity": "sha512-2GfoG+f41kiBIIyd3gF+/GRCCYtamC8/2zlAcD8cqQmqI9Q+YVz7fJLHMmU9pXDVYYHpJeCgUSBJju85vu5q8Q==", + "dependencies": { + "clsx": "^1.1.0", + "prop-types": "^15.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-0 || ^18.0.0" + } + }, "node_modules/react-textarea-autosize": { "version": "8.4.0", "dev": true, @@ -21693,7 +21718,9 @@ "version": "1.0.1" }, "react-markdown": { - "version": "8.0.5", + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-8.0.7.tgz", + "integrity": "sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==", "requires": { "@types/hast": "^2.0.0", "@types/prop-types": "^15.0.0", @@ -21858,6 +21885,21 @@ "tiny-warning": "^1.0.0" } }, + "react-spinners": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.13.8.tgz", + "integrity": "sha512-3e+k56lUkPj0vb5NDXPVFAOkPC//XyhKPJjvcGjyMNPWsBKpplfeyialP74G7H7+It7KzhtET+MvGqbKgAqpZA==", + "requires": {} + }, + "react-tabs": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/react-tabs/-/react-tabs-4.3.0.tgz", + "integrity": "sha512-2GfoG+f41kiBIIyd3gF+/GRCCYtamC8/2zlAcD8cqQmqI9Q+YVz7fJLHMmU9pXDVYYHpJeCgUSBJju85vu5q8Q==", + "requires": { + "clsx": "^1.1.0", + "prop-types": "^15.5.0" + } + }, "react-textarea-autosize": { "version": "8.4.0", "dev": true, diff --git a/package.json b/package.json index ee1c3aeaa..2976b33c0 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,10 @@ "docusaurus-theme-openapi-docs": "^1.5.1", "prism-react-renderer": "^1.3.1", "react": "^17.0.2", - "react-dom": "^17.0.2" + "react-dom": "^17.0.2", + "react-markdown": "^8.0.7", + "react-spinners": "^0.13.8", + "react-tabs": "^4.3.0" }, "overrides": { "mermaid": "9.1.7" diff --git a/src/components/blog/BlogBanner/index.js b/src/components/blog/BlogBanner/index.js new file mode 100644 index 000000000..5f9e5878f --- /dev/null +++ b/src/components/blog/BlogBanner/index.js @@ -0,0 +1,21 @@ +import React from 'react'; +import clsx from 'clsx'; +import styles from './styles.module.css'; +import useBaseUrl from '@docusaurus/useBaseUrl'; +import Link from '@docusaurus/Link'; + +export default function BlogBanner() { + return ( +
+ +
+
+ SailPoint Developer Blog +
+
+ + + +
+ ); +} diff --git a/src/components/blog/BlogBanner/styles.module.css b/src/components/blog/BlogBanner/styles.module.css new file mode 100644 index 000000000..fa4843401 --- /dev/null +++ b/src/components/blog/BlogBanner/styles.module.css @@ -0,0 +1,24 @@ + +.blogHeaderText { + position: relative; + color: #ffffff; + font-size: 48px; + max-width: 396px; + font-weight: bold; + line-height: 133%; + top: 21px; + left: 74px; +} + +.background { + width: 100%; + object-fit: repeat; + height: 100%; +} + +.imageContainer { + width: 100%; + height: 180px; + background-image: url('../../../../static/blog/blog_banner_template.png'); + background-repeat: repeat; +} \ No newline at end of file diff --git a/src/components/blog/BlogCard/index.js b/src/components/blog/BlogCard/index.js new file mode 100644 index 000000000..a3af4a5ac --- /dev/null +++ b/src/components/blog/BlogCard/index.js @@ -0,0 +1,59 @@ +import React from 'react'; +import styles from './styles.module.css'; +import Link from '@docusaurus/Link'; +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; +import {addDarkToFileName} from '../../../util/util'; +export default function BlogCard({ + link, + title, + tags, + creatorImage, + image, + excerpt, + name, + views, + replies, + readTime +}) { + + return ( + +
+ +
+ +
{views}
+ +
{replies}
+ +
{readTime} minute read
+
+ +
+ +
{name}
+
+ + +
+ +
{title}
+
+ {tags?.map((tag, index) => { + if (index > 2) { + return ''; + } + return
{tag}
; + })} +
+
{excerpt}
+
+ + + + +
+ + ); +} diff --git a/src/components/blog/BlogCard/styles.module.css b/src/components/blog/BlogCard/styles.module.css new file mode 100644 index 000000000..19eaa3be2 --- /dev/null +++ b/src/components/blog/BlogCard/styles.module.css @@ -0,0 +1,127 @@ +/* Getting Started Card */ +.card { + position: relative; + margin-top: 20px; + height: 650px; + /* UI Properties */ + background: var(--dev-card-background); + box-shadow: var(--dev-card-shadow); + border: 1px solid var(--dev-card-background); + border-radius: 40px; + opacity: 1; + transition: all 0.3s; + max-width: 600px; +} + +.card:hover { + cursor: pointer; + transform: translate(0px, -5px); + box-shadow: var(--dev-card-selected); +} + + + + + +.cardText { + position: absolute; + margin: 22px; + min-width: 170px; + top: 10px; + left: 0; +} + + +.cardTitle { + font-size: 22px; + font-weight: 700; +} + +.cardBody { + font-size: 16px; + font-weight: 500; + top: 10px; + left: 0; +} + +.tag { + font-size: 16px; + font-weight: 500; + color: var(--dev-secondary-text); + background-color: var(--dev-tag-highlight); + padding: 0px 8px; + margin-left: 5px; + margin-top: 5px; +} + +.tags { + margin: 0px; + width: 100%; + display: flex; + flex-wrap: wrap; +} + + + + + +.cardUser { + position: absolute; + bottom: 10px; + left: 25px; + display: flex; +} + +.cardFace { + border-radius: 9999px; + margin: auto; + height: 40px; + width: 40px; +} + +.cardName { + min-width: 170px; + margin-top: 7px; + margin-left: 10px; +} + + + + + +.cardData { + position: absolute; + bottom: 50px; + left: 20px; + display: flex; + align-items: center; +} + +.cardEye { + fill: #96a9bb; + /* computed from codepen https://codepen.io/sosuke/pen/Pjoqqp */ + filter: invert(79%) sepia(12%) saturate(484%) hue-rotate(168deg) brightness(84%) contrast(84%); + margin-left: 5px; + height: 18px; + width: 18px; +} + +.cardComment { + margin-left: 10px; + fill: #96a9bb; + /* computed from codepen https://codepen.io/sosuke/pen/Pjoqqp */ + filter: invert(79%) sepia(12%) saturate(484%) hue-rotate(168deg) brightness(84%) contrast(84%); + height: 18px; + width: 18px; + margin-bottom: 4px; +} + +.cardCommentText { + color: #96a9ba; + /* computed from codepen https://codepen.io/sosuke/pen/Pjoqqp */ + margin-left: 5px; + margin-bottom: 5px; + height: 20px; + font-size: 16px; +} + diff --git a/src/components/blog/BlogCards/index.js b/src/components/blog/BlogCards/index.js new file mode 100644 index 000000000..530d71dd9 --- /dev/null +++ b/src/components/blog/BlogCards/index.js @@ -0,0 +1,109 @@ +import React from 'react'; +import styles from './styles.module.css'; +import BlogCard from '../BlogCard'; +import BounceLoader from 'react-spinners/BounceLoader'; + +import {getBlogPosts, getTopic} from '../../../services/DiscourseService'; +export default function BlogCards({ + filterCallback + }) { + const [cardData, setCardData] = React.useState(); + const [loadingCards, setLoadingCards] = React.useState(true); + + const getPosts = async () => { + const data = await getBlogPosts(filterCallback.join(',')); + const resultset = [] + if (data.topics) { + for (const topic of data.topics) { + resultset.push(await getPostList(topic)) + } + setCardData(resultset); + } else { + setCardData(undefined); + } + setLoadingCards(false); + }; + + React.useEffect(() => { + getPosts(); + setCardData(undefined); + setLoadingCards(true); + }, [filterCallback]); + + if (cardData) { + return ( +
+
+ {cardData.map(function(a, index){ + return + })} +
+
+ ); + } else if (loadingCards) { + return ( + + ); + } else { + return ( +
+ {' '} + No Blogposts Found with the Given Search Criteria +
+ ); + } +} + +async function getPostList(topic) { + const fullTopic = await getTopic(topic.id); + return { + name: fullTopic.details.created_by.name, + excerpt: styleExcerpt(topic.excerpt), + creatorImage: getavatarURL(fullTopic.details.created_by.avatar_template), + tags: topic.tags, + image: fullTopic.image_url, + link: + 'https://developer.sailpoint.com/discuss/t/' + + topic.slug + + '/' + + topic.id, + title: topic.title, + views: fullTopic.views, + liked: topic.like_count, + replies: fullTopic.posts_count, + solution: topic.has_accepted_answer, + readTime: parseInt(fullTopic.word_count/100), + }; +} + +function getavatarURL(avatar) { + return "https://developer.sailpoint.com" + avatar.replace("{size}", "120") +} + +function styleExcerpt(excerpt) { + if (excerpt.length > 150) { + return excerpt.slice(0, 150) + "..." + } else { + return excerpt.replace("…", "") + } +} + diff --git a/src/components/blog/BlogCards/styles.module.css b/src/components/blog/BlogCards/styles.module.css new file mode 100644 index 000000000..6dea5ccfc --- /dev/null +++ b/src/components/blog/BlogCards/styles.module.css @@ -0,0 +1,35 @@ +/* Getting Started Card container */ +.gridContainer { + display: grid; + place-content: center; + grid-template-columns: repeat(auto-fit, minmax(345px, 1fr)); + grid-gap: 40px; + margin-left: 40px; + margin-right: 40px; +} + +.center { + margin: 0px auto; + max-width: 1300px; + margin-bottom: 50px; +} + +.space { + height: 200px; +} + +.noFound { + font-size: 28px; + font-weight: 500; + color: var(--dev-secondary-text); + padding: 8px; + margin: 50px; +} + + + +.spinnerCenter { + margin: 100px auto; + max-width: 1300px; + margin-bottom: 50px; +} \ No newline at end of file diff --git a/src/components/blog/BlogSidebar/BlogSidebarButton/index.js b/src/components/blog/BlogSidebar/BlogSidebarButton/index.js new file mode 100644 index 000000000..48b9e676a --- /dev/null +++ b/src/components/blog/BlogSidebar/BlogSidebarButton/index.js @@ -0,0 +1,17 @@ +import React from 'react'; +import styles from './styles.module.css'; + + +export default function BlogSidebarButton({ + filterCallback, + text + }) { + const [isActive, setIsActive] = React.useState(false); + const activeClass = isActive ? styles.tagSelected : '' + function setFilters(e, f) { + filterCallback(f) + setIsActive(current => !current); + } + return
setFilters(e, text)} className={activeClass + ' ' + styles.tag}>{text}
+ +} diff --git a/src/components/blog/BlogSidebar/BlogSidebarButton/styles.module.css b/src/components/blog/BlogSidebar/BlogSidebarButton/styles.module.css new file mode 100644 index 000000000..7be4bbadc --- /dev/null +++ b/src/components/blog/BlogSidebar/BlogSidebarButton/styles.module.css @@ -0,0 +1,36 @@ + +.tag { + font-size: 16px; + font-weight: 500; + color: var(--dev-secondary-text); + padding: 8px; + margin-right: 5px; + margin-top: 5px; + margin-bottom: 5px; + border-style: solid; + border-width: 1px; + border-color: var(--dev-text-color-normal); + transition: background-color 500ms; +} + +.tagSelected { + font-size: 16px; + font-weight: 500; + background-color: var(--dev-secondary-text); + color: var(--dev-card-background); + padding: 8px; + margin-right: 5px; + margin-bottom: 5px; + margin-top: 5px; + border-style: solid; + border-width: 1px; + border-color: var(--dev-text-color-normal); + transition: background-color 500ms; +} + +.tag:hover { + cursor: pointer; + background-color: var(--dev-text-color-normal); + color: var(--dev-card-background); +} + diff --git a/src/components/blog/BlogSidebar/index.js b/src/components/blog/BlogSidebar/index.js new file mode 100644 index 000000000..e25b75e96 --- /dev/null +++ b/src/components/blog/BlogSidebar/index.js @@ -0,0 +1,72 @@ +import React from 'react'; +import clsx from 'clsx'; +import styles from './styles.module.css'; +import useBaseUrl from '@docusaurus/useBaseUrl'; +import Link from '@docusaurus/Link'; +import { getTags } from '../../../services/DiscourseService'; +import BlogSidebarButton from './BlogSidebarButton'; + +export default function BlogSidebar({ + filterCallback + }) { + const [tagProductData, setTagProductData] = React.useState(); + const [tagTechnologyData, setTagTechnologyData] = React.useState(); + const [filterTags, setFilterTags] = React.useState(true); + + const getTagData = async () => { + const data = await getTags(); + const tagTechnologyResultset = [] + const tagProductResultset = [] + for (const tagGroup of data.extras.tag_groups) { + if (tagGroup.id === 20) { + for (const tag of tagGroup.tags) { + tagProductResultset.push(tag.text) + } + } + if (tagGroup.id === 17) { + for (const tag of tagGroup.tags) { + tagTechnologyResultset.push(tag.text) + } + } + } + setTagProductData(tagProductResultset) + setTagTechnologyData(tagTechnologyResultset) + }; + + function toggleSeeAll() { + filterTags ? setFilterTags(false) : setFilterTags(true) + } + + + React.useEffect(() => { + getTagData(); + }, []); + + const filterText = filterTags ? 'See All Tags' : 'See Less Tags' + + if (tagProductData && tagTechnologyData) { + return ( +
+
Posts by Product
+
+ {tagProductData.map(function(a, index){ + return + })} +
+
Posts by Identity Governance
+
+ {tagTechnologyData.map(function(a, index){ + return
10 && filterTags ? styles.hidden : ''} >
+ })} +
+
toggleSeeAll()}> + {filterText} + {/* */} +
+
+ + ); + } else { + return
; + } +} diff --git a/src/components/blog/BlogSidebar/styles.module.css b/src/components/blog/BlogSidebar/styles.module.css new file mode 100644 index 000000000..4a15ed7d7 --- /dev/null +++ b/src/components/blog/BlogSidebar/styles.module.css @@ -0,0 +1,40 @@ + +.sidebar { + width: 400px; + height: 100%; + margin-left: 50px; +} + +.tagHeader { + margin-top: 10px; + font-size: 22px; + font-weight: 700; +} + +.tagContainer { + display: flex; + flex-wrap: wrap; +} + + +.hidden { + display: none; +} + +.seeAll { + margin-top: 5px; + margin-bottom: 10px; + color: var(--dev-secondary-text); + border-style: solid; + border-width: 1px; + width: 100%; + transition: background-color 500ms; + text-align: center; + +} + +.seeAll:hover { + cursor: pointer; + background-color: var(--dev-text-color-normal); + color: var(--dev-card-background); +} diff --git a/src/components/homepage/DiscussCard/index.js b/src/components/homepage/DiscussCard/index.js index 769c6087f..f9ed059a6 100644 --- a/src/components/homepage/DiscussCard/index.js +++ b/src/components/homepage/DiscussCard/index.js @@ -64,7 +64,7 @@ export default function DiscussCard({ if (index > 2) { return ''; } - return
{tag}
; + return
{tag}
; })} diff --git a/src/components/marketplace/MarketplaceBanner/index.js b/src/components/marketplace/MarketplaceBanner/index.js new file mode 100644 index 000000000..8c9e2cacd --- /dev/null +++ b/src/components/marketplace/MarketplaceBanner/index.js @@ -0,0 +1,21 @@ +import React from 'react'; +import clsx from 'clsx'; +import styles from './styles.module.css'; +import useBaseUrl from '@docusaurus/useBaseUrl'; +import Link from '@docusaurus/Link'; + +export default function MarketplaceBanner() { + return ( +
+ +
+
+ SailPoint Developer Marketplace +
+
+ + + +
+ ); +} diff --git a/src/components/marketplace/MarketplaceBanner/styles.module.css b/src/components/marketplace/MarketplaceBanner/styles.module.css new file mode 100644 index 000000000..447c3ecb9 --- /dev/null +++ b/src/components/marketplace/MarketplaceBanner/styles.module.css @@ -0,0 +1,25 @@ + +.blogHeaderText { + position: relative; + color: #ffffff; + font-size: 48px; + max-width: 459px; + font-weight: bold; + line-height: 130%; + top: 29px; + left: 228px; +} + +.background { + width: 100%; + object-fit: repeat; + height: 100%; +} + +.imageContainer { + width: 100%; + height: 180px; + background-size: 1920px 180px; + background-image: url('../../../../static/blog/marketplace_banner_template.png'); + background-repeat: repeat; +} \ No newline at end of file diff --git a/src/components/marketplace/MarketplaceCard/index.js b/src/components/marketplace/MarketplaceCard/index.js new file mode 100644 index 000000000..1c40f9af8 --- /dev/null +++ b/src/components/marketplace/MarketplaceCard/index.js @@ -0,0 +1,68 @@ +import React from 'react'; +import styles from './styles.module.css'; +import Link from '@docusaurus/Link'; +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; +import {addDarkToFileName} from '../../../util/util'; +import ReactMarkdown from 'react-markdown' +export default function MarketplaceCard({ + post, + openDialogFunc, +}) { + + function setFilters(e) { + openDialogFunc({"title": post.title, "image": post.image, "link": post.link, "id": post.id}); + } + + let badge = ( +
+ ); + if (post.tags.includes("sailpoint-authored")) { + badge = ( + + ); + } + + + return ( +
setFilters(e)}> +
+ +
+ +
{post.views}
+ +
{post.replies}
+
+ +
+ +
{post.name}
+
+ + + {badge} + +
+ +
{post.title}
+
+ {post.tags?.map((tag, index) => { + if (index > 2) { + return ''; + } + return
{tag}
; + })} +
+ {post.excerpt} +
+ + + + +
+
+ ); +} diff --git a/src/components/marketplace/MarketplaceCard/styles.module.css b/src/components/marketplace/MarketplaceCard/styles.module.css new file mode 100644 index 000000000..5dd9f3464 --- /dev/null +++ b/src/components/marketplace/MarketplaceCard/styles.module.css @@ -0,0 +1,138 @@ +/* Getting Started Card */ +.card { + position: relative; + margin-top: 20px; + height: 650px; + /* UI Properties */ + background: var(--dev-card-background); + box-shadow: var(--dev-card-shadow); + border: 1px solid var(--dev-card-background); + border-radius: 40px; + opacity: 1; + transition: all 0.3s; + max-width: 600px; +} + +.card:hover { + cursor: pointer; + transform: translate(0px, -5px); + box-shadow: var(--dev-card-selected); +} + + + + + +.cardText { + position: absolute; + margin: 22px; + min-width: 170px; + top: 10px; + left: 0; +} + + +.cardTitle { + font-size: 22px; + font-weight: 700; +} + +.cardBody { + font-size: 16px; + font-weight: 500; + top: 10px; + left: 0; +} + +.tag { + font-size: 16px; + font-weight: 500; + color: var(--dev-secondary-text); + background-color: var(--dev-tag-highlight); + padding: 0px 8px; + margin-left: 5px; + margin-top: 5px; +} + +.tags { + margin: 0px; + width: 100%; + display: flex; + flex-wrap: wrap; +} + + + + + +.cardUser { + position: absolute; + bottom: 10px; + left: 25px; + display: flex; +} + +.cardFace { + border-radius: 9999px; + margin: auto; + height: 40px; + width: 40px; +} + +.cardName { + min-width: 170px; + margin-top: 7px; + margin-left: 10px; +} + + + + + +.cardData { + position: absolute; + bottom: 50px; + left: 20px; + display: flex; + align-items: center; +} + +.cardEye { + fill: #96a9bb; + /* computed from codepen https://codepen.io/sosuke/pen/Pjoqqp */ + filter: invert(79%) sepia(12%) saturate(484%) hue-rotate(168deg) brightness(84%) contrast(84%); + margin-left: 5px; + height: 18px; + width: 18px; +} + +.cardComment { + margin-left: 10px; + fill: #96a9bb; + /* computed from codepen https://codepen.io/sosuke/pen/Pjoqqp */ + filter: invert(79%) sepia(12%) saturate(484%) hue-rotate(168deg) brightness(84%) contrast(84%); + height: 18px; + width: 18px; + margin-bottom: 4px; +} + +.cardCommentText { + color: #96a9ba; + /* computed from codepen https://codepen.io/sosuke/pen/Pjoqqp */ + margin-left: 5px; + margin-bottom: 5px; + height: 20px; + font-size: 16px; +} + + + + +.cardBadge { + position: absolute; + right: 5px; + bottom: 0px; + margin-left: 5px; + height: 120px; + width: 120px; +} \ No newline at end of file diff --git a/src/components/marketplace/MarketplaceCardDetail/index.js b/src/components/marketplace/MarketplaceCardDetail/index.js new file mode 100644 index 000000000..037f8dda9 --- /dev/null +++ b/src/components/marketplace/MarketplaceCardDetail/index.js @@ -0,0 +1,108 @@ +import React from 'react'; +import styles from './styles.module.css'; +import Link from '@docusaurus/Link'; +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; +import {addDarkToFileName} from '../../../util/util'; +import {Tab, Tabs, TabList, TabPanel} from 'react-tabs'; +import 'react-tabs/style/react-tabs.css'; +import ReactMarkdown from 'react-markdown'; +import BounceLoader from 'react-spinners/BounceLoader'; +export default function MarketplaceCardDetail({details, rawPost}) { + const getDivText = (data, id) => { + const requirementPosition = data.indexOf('id="' + id + '"'); + const requirementEndPosition = data.indexOf('', requirementPosition); + if (requirementPosition > 0 && requirementEndPosition > 0) { + const validContent = data.substring( + requirementPosition + 6 + id.length, + requirementEndPosition, + ); + const incorrectURLPattern = /upload:\/\/([^"]+)/g; + const correctURLPattern = + 'https://developer.identitysoon.com/discuss/uploads/short-url/$1'; + return validContent.replace(incorrectURLPattern, correctURLPattern); + } else { + return 'No requirements found for this marketplace item'; + } + // https://developer.identitysoon.com/discuss/uploads/short-url/f9rlt0eoj7RnpndvL0CHjcEesV0.png?dl=1\ + // upload://f9rlt0eoj7RnpndvL0CHjcEesV0.png + }; + + const goToLink = (link) => { + window.open(link, '_blank'); + }; + + if (details) { + return ( +
+
+
{details.title}
+ +
+ + + + Details + Requirements + Support + + + + + {getDivText(rawPost, 'details')} + + + + + + + {getDivText(rawPost, 'requirements')} + + + + + {getDivText(rawPost, 'support')} + + + +
+ ); + } else { + return ( + + ); + } +} diff --git a/src/components/marketplace/MarketplaceCardDetail/styles.module.css b/src/components/marketplace/MarketplaceCardDetail/styles.module.css new file mode 100644 index 000000000..cd921b7da --- /dev/null +++ b/src/components/marketplace/MarketplaceCardDetail/styles.module.css @@ -0,0 +1,80 @@ +.detailContainer { + max-width: 937px; +} + +.detailHeader { + display: flex; + justify-content: center; + align-items: center; + margin: 20px; +} + +.detailImage { + border-radius: 40px; + max-width: 400px; + min-width: 180px; +} + +.detailTitle { + margin: 45px; + font-size: 48px; + font-weight: 500; + color: var(--dev-secondary-text); + text-shadow: rgba(17, 61, 158, 0.64) 1px 2px 2px; +} + +.detailTabContent { + margin: 20px; +} + +.detailTabs { + margin: 10px; +} + + +.modalButtonText { + position: relative; + top: -2px; +} + +.modalButton { + border: 1px solid var(--ifm-color-primary); + box-shadow: 1px 4px 10px rgba(0, 0, 0, 0.12); + border-radius: 24px; + color: var(--ifm-color-primary); + margin: 10px; + padding: 10px; + text-align: center; + width: 125px; + background-color: #ffffff31; +} + +.modalButton:hover { + cursor: pointer; + top: -2px; + box-shadow: 0 4px 5px rgba(0, 0, 0, 0.4); + background-color: #dae1e9; + color: #005fc4; +} + +.spinnerCenter { + margin: 30vh 20vh; +} + +@media only screen and (max-width: 768px) { + .detailImage { + display: none; + } +} + +.buttonImage { + position: relative; + left: -7px; + top: 4px; + fill: #96a9bb; + /* computed from codepen https://codepen.io/sosuke/pen/Pjoqqp */ + filter: invert(79%) sepia(12%) saturate(484%) hue-rotate(168deg) brightness(84%) contrast(84%); + margin-left: 5px; + height: 18px; + width: 18px; +} \ No newline at end of file diff --git a/src/components/marketplace/MarketplaceCards/index.js b/src/components/marketplace/MarketplaceCards/index.js new file mode 100644 index 000000000..3dd5ca268 --- /dev/null +++ b/src/components/marketplace/MarketplaceCards/index.js @@ -0,0 +1,154 @@ +import React from 'react'; +import styles from './styles.module.css'; +import MarketplaceCard from '../MarketplaceCard'; +import Modal from 'react-modal'; +import useBaseUrl from '@docusaurus/useBaseUrl'; +import BounceLoader from 'react-spinners/BounceLoader'; + +import { + getMarketplacePosts, + getMarketplaceTopic, + getMarketplaceTopicRaw, +} from '../../../services/DiscourseService'; +import MarketplaceCardDetail from '../MarketplaceCardDetail'; +export default function MarketplaceCards({filterCallback}) { + const [cardData, setCardData] = React.useState(); + const [detailsOpen, setDetailsOpen] = React.useState(false); + const [details, setDetails] = React.useState(''); + const [loadingCards, setLoadingCards] = React.useState(true); + + const getPosts = async () => { + const data = await getMarketplacePosts(filterCallback.join(',')); + console.log(data) + const resultset = []; + if (data.topics) { + for (const topic of data.topics) { + if (topic.tags.length > 0) { + resultset.push(await getPostList(topic)); + } + } + setCardData(resultset); + } else { + setCardData(undefined); + } + setLoadingCards(false); + }; + + const getDetails = async (data) => { + const raw = await getMarketplaceTopicRaw(data.id); + setDetails({data: data, raw: raw}); + }; + + // callback that gets called when clicking on a card + const openDialog = (data) => { + setDetails({data: undefined, raw: undefined}); + getDetails(data); + setDetailsOpen(true); + }; + + React.useEffect(() => {}, [detailsOpen, details]); + + Modal.setAppElement('#__docusaurus'); + React.useEffect(() => { + getPosts(); + setCardData(undefined); + setLoadingCards(true); + }, [filterCallback]); + + + const xImage = useBaseUrl('/icons/circle-xmark-regular.svg') + + if (cardData) { + return ( +
+
+ {cardData.map(function (a, index) { + return ( + + ); + })} +
+ +
+
+ +
+ { + setDetailsOpen(false); + }}> +
+
+
+ ); + } else if (loadingCards) { + return ( + + ); + } else { + return ( +
+ {' '} + No Marketplace Item Found with the Given Search Criteria +
+ ); + } +} + +async function getPostList(topic) { + const fullTopic = await getMarketplaceTopic(topic.id); + return { + id: topic.id, + name: fullTopic.details.created_by.name, + excerpt: styleExcerpt(topic.excerpt), + creatorImage: getavatarURL(fullTopic.details.created_by.avatar_template), + tags: topic.tags, + image: fullTopic.image_url, + link: + 'https://developer.sailpoint.com/discuss/t/' + + topic.slug + + '/' + + topic.id, + title: topic.title, + views: fullTopic.views, + liked: topic.like_count, + replies: fullTopic.posts_count, + solution: topic.has_accepted_answer, + readTime: parseInt(fullTopic.word_count / 100), + }; +} + +function getavatarURL(avatar) { + return 'https://developer.sailpoint.com' + avatar.replace('{size}', '120'); +} + +function styleExcerpt(excerpt) { + if (excerpt) { + // remove any strings that have colons between them + excerpt = excerpt.replace(/:[^:]*:/g, ''); + if (excerpt.length > 150) { + return excerpt.slice(0, 100) + '...'; + } else { + return excerpt.replace('…', ''); + } + } else { + return ''; + } +} diff --git a/src/components/marketplace/MarketplaceCards/styles.module.css b/src/components/marketplace/MarketplaceCards/styles.module.css new file mode 100644 index 000000000..549642a08 --- /dev/null +++ b/src/components/marketplace/MarketplaceCards/styles.module.css @@ -0,0 +1,78 @@ +/* Getting Started Card container */ +.gridContainer { + display: grid; + place-content: center; + grid-template-columns: repeat(auto-fit, minmax(345px, 1fr)); + grid-gap: 40px; + margin-left: 40px; + margin-right: 40px; +} + +.center { + margin: 0px auto; + max-width: 1300px; + margin-bottom: 50px; +} + +.space { + height: 200px; +} + +.noFound { + font-size: 28px; + font-weight: 500; + color: var(--dev-secondary-text); + padding: 8px; + margin: 50px; +} + + + + +.modal { + position: absolute; + top: 50%; + left: 50%; + right: auto; + bottom: auto; + margin-right: -50%; + transform: translate(-50%, -50%); + box-shadow: 2px 3px 10px rgba(0, 0, 0, 0.25); + border-radius: 20px; + background-color: var(--dev-card-background); + max-height: 100%; + max-width: 90%; + min-height: 80%; + scroll-behavior: auto; + overflow-y: auto; +} + + + + +.cardExit { + position: absolute; + top: 6px; + right: 6px; + fill: #96a9bb; + /* computed from codepen https://codepen.io/sosuke/pen/Pjoqqp */ + filter: invert(79%) sepia(12%) saturate(484%) hue-rotate(168deg) brightness(84%) contrast(84%); + margin-left: 5px; + height: 24px; + width: 24px; + transition: all 0.2s; +} + +.cardExit:hover { + top: 8px; + cursor: pointer; + height: 23px; + width: 23px; +} + + +.spinnerCenter { + margin: 100px auto; + max-width: 1300px; + margin-bottom: 50px; +} \ No newline at end of file diff --git a/src/components/marketplace/MarketplaceSidebar/MarketplaceSidebarButton/index.js b/src/components/marketplace/MarketplaceSidebar/MarketplaceSidebarButton/index.js new file mode 100644 index 000000000..d6c84eb81 --- /dev/null +++ b/src/components/marketplace/MarketplaceSidebar/MarketplaceSidebarButton/index.js @@ -0,0 +1,17 @@ +import React from 'react'; +import styles from './styles.module.css'; + + +export default function MarketplaceSidebarButton({ + filterCallback, + text + }) { + const [isActive, setIsActive] = React.useState(false); + const activeClass = isActive ? styles.tagSelected : '' + function setFilters(e, f) { + filterCallback(f) + setIsActive(current => !current); + } + return
setFilters(e, text)} className={activeClass + ' ' + styles.tag}>{text}
+ +} diff --git a/src/components/marketplace/MarketplaceSidebar/MarketplaceSidebarButton/styles.module.css b/src/components/marketplace/MarketplaceSidebar/MarketplaceSidebarButton/styles.module.css new file mode 100644 index 000000000..7be4bbadc --- /dev/null +++ b/src/components/marketplace/MarketplaceSidebar/MarketplaceSidebarButton/styles.module.css @@ -0,0 +1,36 @@ + +.tag { + font-size: 16px; + font-weight: 500; + color: var(--dev-secondary-text); + padding: 8px; + margin-right: 5px; + margin-top: 5px; + margin-bottom: 5px; + border-style: solid; + border-width: 1px; + border-color: var(--dev-text-color-normal); + transition: background-color 500ms; +} + +.tagSelected { + font-size: 16px; + font-weight: 500; + background-color: var(--dev-secondary-text); + color: var(--dev-card-background); + padding: 8px; + margin-right: 5px; + margin-bottom: 5px; + margin-top: 5px; + border-style: solid; + border-width: 1px; + border-color: var(--dev-text-color-normal); + transition: background-color 500ms; +} + +.tag:hover { + cursor: pointer; + background-color: var(--dev-text-color-normal); + color: var(--dev-card-background); +} + diff --git a/src/components/marketplace/MarketplaceSidebar/index.js b/src/components/marketplace/MarketplaceSidebar/index.js new file mode 100644 index 000000000..f62bdaf67 --- /dev/null +++ b/src/components/marketplace/MarketplaceSidebar/index.js @@ -0,0 +1,72 @@ +import React from 'react'; +import clsx from 'clsx'; +import styles from './styles.module.css'; +import useBaseUrl from '@docusaurus/useBaseUrl'; +import Link from '@docusaurus/Link'; +import { getTags } from '../../../services/DiscourseService'; +import MarketplaceSidebarButton from './MarketplaceSidebarButton'; + +export default function MarketplaceSidebar({ + filterCallback + }) { + const [tagProductData, setTagProductData] = React.useState(); + const [tagTechnologyData, setTagTechnologyData] = React.useState(); + const [filterTags, setFilterTags] = React.useState(true); + + const getTagData = async () => { + const data = await getTags(); + const tagTechnologyResultset = [] + const tagProductResultset = [] + for (const tagGroup of data.extras.tag_groups) { + if (tagGroup.id === 20) { + for (const tag of tagGroup.tags) { + tagProductResultset.push(tag.text) + } + } + if (tagGroup.id === 17) { + for (const tag of tagGroup.tags) { + tagTechnologyResultset.push(tag.text) + } + } + } + setTagProductData(tagProductResultset) + setTagTechnologyData(tagTechnologyResultset) + }; + + function toggleSeeAll() { + filterTags ? setFilterTags(false) : setFilterTags(true) + } + + + React.useEffect(() => { + getTagData(); + }, []); + + const filterText = filterTags ? 'See All Tags' : 'See Less Tags' + + if (tagProductData && tagTechnologyData) { + return ( +
+
Items by Product
+
+ {tagProductData.map(function(a, index){ + return + })} +
+
Items by Identity Governance
+
+ {tagTechnologyData.map(function(a, index){ + return
10 && filterTags ? styles.hidden : ''} >
+ })} +
+
toggleSeeAll()}> + {filterText} + {/* */} +
+
+ + ); + } else { + return
; + } +} diff --git a/src/components/marketplace/MarketplaceSidebar/styles.module.css b/src/components/marketplace/MarketplaceSidebar/styles.module.css new file mode 100644 index 000000000..4a15ed7d7 --- /dev/null +++ b/src/components/marketplace/MarketplaceSidebar/styles.module.css @@ -0,0 +1,40 @@ + +.sidebar { + width: 400px; + height: 100%; + margin-left: 50px; +} + +.tagHeader { + margin-top: 10px; + font-size: 22px; + font-weight: 700; +} + +.tagContainer { + display: flex; + flex-wrap: wrap; +} + + +.hidden { + display: none; +} + +.seeAll { + margin-top: 5px; + margin-bottom: 10px; + color: var(--dev-secondary-text); + border-style: solid; + border-width: 1px; + width: 100%; + transition: background-color 500ms; + text-align: center; + +} + +.seeAll:hover { + cursor: pointer; + background-color: var(--dev-text-color-normal); + color: var(--dev-card-background); +} diff --git a/src/css/custom.css b/src/css/custom.css index d543c0a18..31ad70df0 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -244,6 +244,7 @@ --dev-text-color-secondary: #0033a1; --dev-secondary-text: #415364; + --dev-tag-highlight: #eaeef1; --text-on-primary: #ffffff; @@ -286,6 +287,7 @@ --dev-card-shadow: 0 4px 5px rgba(0, 0, 0, 0.2); --dev-card-selected: 0 5px 5px rgba(107, 107, 107, 0.2); --dev-secondary-text: #dae1e9; + --dev-tag-highlight: #00000075; /*main hero card*/ --main-hero-card-background: #202122; diff --git a/src/pages/blog.js b/src/pages/blog.js new file mode 100644 index 000000000..b8e30ec62 --- /dev/null +++ b/src/pages/blog.js @@ -0,0 +1,41 @@ +import React from 'react'; +import clsx from 'clsx'; +import Link from '@docusaurus/Link'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import Layout from '@theme/Layout'; +import BlogBanner from '../components/blog/BlogBanner'; + +import styles from './blog.module.css'; +import BlogCards from '../components/blog/BlogCards'; +import BlogSidebar from '../components/blog/BlogSidebar'; + +export default function Blog() { + const [filteredProduct, setFilteredProduct] = React.useState([]); + + const {siteConfig} = useDocusaurusContext(); + + const handleClick = (data) => { + var tempFilter = filteredProduct.slice() + + const index = tempFilter.indexOf(data); + if (index !== -1) { + tempFilter.splice(index, 1); + } else { + tempFilter.push(data) + } + + setFilteredProduct(tempFilter) + }; + return ( + +
+ +
+
+
+
+ +
+
+ ); +} diff --git a/src/pages/blog.module.css b/src/pages/blog.module.css new file mode 100644 index 000000000..d16546f19 --- /dev/null +++ b/src/pages/blog.module.css @@ -0,0 +1,17 @@ +.blogContainer { + display: flex; +} + +.blogSidbarContainer { + flex: 5%; +} + +@media only screen and (max-width: 870px) { + .blogSidbarContainer { + display: none; + } +} + +.blogCardContainer { + flex: 95%; +} \ No newline at end of file diff --git a/src/pages/marketplace.js b/src/pages/marketplace.js new file mode 100644 index 000000000..0bd99ac98 --- /dev/null +++ b/src/pages/marketplace.js @@ -0,0 +1,41 @@ +import React from 'react'; +import clsx from 'clsx'; +import Link from '@docusaurus/Link'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import Layout from '@theme/Layout'; +import MarketplaceBanner from '../components/marketplace/MarketplaceBanner'; + +import styles from './marketplace.module.css'; +import MarketplaceCards from '../components/marketplace/MarketplaceCards'; +import MarketplaceSidebar from '../components/marketplace/MarketplaceSidebar'; + +export default function Marketplace() { + const [filteredProduct, setFilteredProduct] = React.useState([]); + + const {siteConfig} = useDocusaurusContext(); + + const handleClick = (data) => { + var tempFilter = filteredProduct.slice() + + const index = tempFilter.indexOf(data); + if (index !== -1) { + tempFilter.splice(index, 1); + } else { + tempFilter.push(data) + } + + setFilteredProduct(tempFilter) + }; + return ( + +
+ +
+
+
+
+ +
+
+ ); +} diff --git a/src/pages/marketplace.module.css b/src/pages/marketplace.module.css new file mode 100644 index 000000000..d16546f19 --- /dev/null +++ b/src/pages/marketplace.module.css @@ -0,0 +1,17 @@ +.blogContainer { + display: flex; +} + +.blogSidbarContainer { + flex: 5%; +} + +@media only screen and (max-width: 870px) { + .blogSidbarContainer { + display: none; + } +} + +.blogCardContainer { + flex: 95%; +} \ No newline at end of file diff --git a/src/services/DiscourseService.js b/src/services/DiscourseService.js index 194f97876..7879b1cb4 100644 --- a/src/services/DiscourseService.js +++ b/src/services/DiscourseService.js @@ -8,3 +8,81 @@ export async function getTopPosts() { return []; } } + +export async function getBlogPosts(tags) { + let url = '' + if (tags) { + url = 'https://developer.sailpoint.com/discuss/search.json?q=category:blog+tags:' + tags + } else { + url = 'https://developer.sailpoint.com/discuss/search.json?q=category:blog' + } + try { + const response = await fetch( + url, + ); + return await response.json(); + } catch (error) { + return []; + } +} + +export async function getMarketplacePosts(tags) { + let url = '' + if (tags) { + url = 'https://developer.sailpoint.com/discuss/search.json?q=category:marketplace+tags:' + tags + } else { + url = 'https://developer.sailpoint.com/discuss/search.json?q=category:marketplace' + } + try { + const response = await fetch( + url, + ); + return await response.json(); + } catch (error) { + return []; + } +} + +export async function getTopic(id) { + try { + const response = await fetch( + 'https://developer.sailpoint.com/discuss/t/' + id + '.json', + ); + return await response.json(); + } catch (error) { + return []; + } +} + +export async function getMarketplaceTopic(id) { + try { + const response = await fetch( + 'https://developer.sailpoint.com/discuss/t/' + id + '.json', + ); + return await response.json(); + } catch (error) { + return []; + } +} + +export async function getMarketplaceTopicRaw(id) { + try { + const response = await fetch( + 'https://developer.sailpoint.com/discuss/raw/' + id + '.json', + ); + return await response.text(); + } catch (error) { + return []; + } +} + +export async function getTags() { + try { + const response = await fetch( + 'https://developer.sailpoint.com/discuss/tags.json', + ); + return await response.json(); + } catch (error) { + return []; + } +} \ No newline at end of file diff --git a/static/blog/Identity-Security-Cloud-Platform.png b/static/blog/Identity-Security-Cloud-Platform.png new file mode 100644 index 000000000..1424af27c Binary files /dev/null and b/static/blog/Identity-Security-Cloud-Platform.png differ diff --git a/static/blog/blog_banner_template.png b/static/blog/blog_banner_template.png new file mode 100644 index 000000000..9859513c0 Binary files /dev/null and b/static/blog/blog_banner_template.png differ diff --git a/static/blog/caret-down-thin.svg b/static/blog/caret-down-thin.svg new file mode 100644 index 000000000..b97785a6a --- /dev/null +++ b/static/blog/caret-down-thin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/blog/clock-light.svg b/static/blog/clock-light.svg new file mode 100644 index 000000000..e28560de9 --- /dev/null +++ b/static/blog/clock-light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/blog/comment-light.svg b/static/blog/comment-light.svg new file mode 100644 index 000000000..41509edf3 --- /dev/null +++ b/static/blog/comment-light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/blog/comment-regular.svg b/static/blog/comment-regular.svg new file mode 100644 index 000000000..9ef59bdbd --- /dev/null +++ b/static/blog/comment-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/blog/eye-regular.svg b/static/blog/eye-regular.svg new file mode 100644 index 000000000..7eccb4333 --- /dev/null +++ b/static/blog/eye-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/blog/marketplace_banner_template.png b/static/blog/marketplace_banner_template.png new file mode 100644 index 000000000..d85daa14a Binary files /dev/null and b/static/blog/marketplace_banner_template.png differ diff --git a/static/blog/watch-light.svg b/static/blog/watch-light.svg new file mode 100644 index 000000000..cea5a24d4 --- /dev/null +++ b/static/blog/watch-light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/icons/SailPoint-LogoIcon-RGB-Color.svg b/static/icons/SailPoint-LogoIcon-RGB-Color.svg new file mode 100644 index 000000000..858328dfd --- /dev/null +++ b/static/icons/SailPoint-LogoIcon-RGB-Color.svg @@ -0,0 +1,466 @@ + + + + + + + + + + +]> + + + + + + + + + + + + + + + + + + + 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 +jvyOGtKLkonVP53m+h2CKf2zmWOfowliBw== + + + diff --git a/static/icons/circle-xmark-regular.svg b/static/icons/circle-xmark-regular.svg new file mode 100644 index 000000000..ca484b8d3 --- /dev/null +++ b/static/icons/circle-xmark-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/icons/discourse.svg b/static/icons/discourse.svg new file mode 100644 index 000000000..77a6b1de3 --- /dev/null +++ b/static/icons/discourse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/icons/github.svg b/static/icons/github.svg new file mode 100644 index 000000000..40e8178e4 --- /dev/null +++ b/static/icons/github.svg @@ -0,0 +1 @@ + \ No newline at end of file