Merge pull request #261 from sailpoint-oss/feature/marketplace

Feature/marketplace
This commit is contained in:
Colin McKibben
2023-06-08 13:08:51 -04:00
committed by GitHub
45 changed files with 2100 additions and 7 deletions

View File

@@ -70,7 +70,7 @@ module.exports = {
{ {
position: 'left', position: 'left',
label: 'Blog', label: 'Blog',
to: 'https://medium.com/sailpointengineering', to: '/blog',
}, },
{ {
position: 'left', position: 'left',

50
package-lock.json generated
View File

@@ -19,7 +19,10 @@
"docusaurus-theme-openapi-docs": "^1.5.1", "docusaurus-theme-openapi-docs": "^1.5.1",
"prism-react-renderer": "^1.3.1", "prism-react-renderer": "^1.3.1",
"react": "^17.0.2", "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": { "devDependencies": {
"@docusaurus/core": "2.2.0", "@docusaurus/core": "2.2.0",
@@ -11024,8 +11027,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/react-markdown": { "node_modules/react-markdown": {
"version": "8.0.5", "version": "8.0.7",
"license": "MIT", "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-8.0.7.tgz",
"integrity": "sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==",
"dependencies": { "dependencies": {
"@types/hast": "^2.0.0", "@types/hast": "^2.0.0",
"@types/prop-types": "^15.0.0", "@types/prop-types": "^15.0.0",
@@ -11302,6 +11306,27 @@
"react": ">=15" "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": { "node_modules/react-textarea-autosize": {
"version": "8.4.0", "version": "8.4.0",
"dev": true, "dev": true,
@@ -21693,7 +21718,9 @@
"version": "1.0.1" "version": "1.0.1"
}, },
"react-markdown": { "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": { "requires": {
"@types/hast": "^2.0.0", "@types/hast": "^2.0.0",
"@types/prop-types": "^15.0.0", "@types/prop-types": "^15.0.0",
@@ -21858,6 +21885,21 @@
"tiny-warning": "^1.0.0" "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": { "react-textarea-autosize": {
"version": "8.4.0", "version": "8.4.0",
"dev": true, "dev": true,

View File

@@ -32,7 +32,10 @@
"docusaurus-theme-openapi-docs": "^1.5.1", "docusaurus-theme-openapi-docs": "^1.5.1",
"prism-react-renderer": "^1.3.1", "prism-react-renderer": "^1.3.1",
"react": "^17.0.2", "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": { "overrides": {
"mermaid": "9.1.7" "mermaid": "9.1.7"

View File

@@ -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 (
<div>
<div className={styles.imageContainer}>
<div className={styles.blogHeaderText}>
SailPoint Developer Blog
</div>
</div >
</div>
);
}

View File

@@ -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;
}

View File

@@ -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 (
<Link to={link}>
<div className={styles.card} >
<div className={styles.cardData}>
<img className={styles.cardEye} src={useBaseUrl('/blog/eye-regular.svg')}></img>
<div className={styles.cardCommentText}>{views}</div>
<img className={styles.cardComment} src={useBaseUrl('/blog/comment-light.svg')}></img>
<div className={styles.cardCommentText}>{replies}</div>
<img className={styles.cardComment} src={useBaseUrl('/blog/clock-light.svg')}></img>
<div className={styles.cardCommentText}>{readTime} minute read</div>
</div>
<div className={styles.cardUser}>
<img className={styles.cardFace} src={useBaseUrl(creatorImage)}></img>
<div className={styles.cardName}>{name}</div>
</div>
<div className={styles.cardText}>
<img className={styles.cardImage} src={useBaseUrl(image)}></img>
<div className={styles.cardTitle}>{title}</div>
<div className={styles.tags}>
{tags?.map((tag, index) => {
if (index > 2) {
return '';
}
return <div key={tag} className={styles.tag}>{tag}</div>;
})}
</div>
<div className={styles.cardBody}>{excerpt}</div>
</div>
</div>
</Link>
);
}

View File

@@ -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;
}

View File

@@ -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 (
<div className={styles.center}>
<div className={styles.gridContainer}>
{cardData.map(function(a, index){
return <BlogCard
key={a.link}
id={index + a.link}
excerpt={a.excerpt}
name={a.name}
tags={a.tags}
link={a.link}
image={a.image}
title={a.title}
views={a.views}
replies={a.replies}
readTime={a.readTime}
creatorImage={a.creatorImage}></BlogCard>
})}
</div>
</div>
);
} else if (loadingCards) {
return (
<BounceLoader
className={styles.spinnerCenter}
color={'#0033a1'}
loading={true}
size={150}
aria-label="Loading Spinner"
data-testid="loader"
/>
);
} else {
return (
<div className={styles.noFound}>
{' '}
No Blogposts Found with the Given Search Criteria
</div>
);
}
}
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("&hellip;", "")
}
}

View File

@@ -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;
}

View File

@@ -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 <div key={text} onClick={(e) => setFilters(e, text)} className={activeClass + ' ' + styles.tag}>{text}</div>
}

View File

@@ -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);
}

View File

@@ -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 (
<div className={styles.sidebar}>
<div className={styles.tagHeader}>Posts by Product</div>
<div className={styles.tagContainer}>
{tagProductData.map(function(a, index){
return <BlogSidebarButton key={a} text={a} filterCallback={filterCallback}></BlogSidebarButton>
})}
</div>
<div className={styles.tagHeader}>Posts by Identity Governance</div>
<div className={styles.tagContainer}>
{tagTechnologyData.map(function(a, index){
return <div key={'div' + a} className={index > 10 && filterTags ? styles.hidden : ''} > <BlogSidebarButton key={a} text={a} filterCallback={filterCallback}></BlogSidebarButton></div>
})}
</div>
<div className={styles.seeAll} onClick={(e) => toggleSeeAll()}>
{filterText}
{/* <img className={styles.caretDown} src={useBaseUrl('/blog/caret-down-thin.svg')}></img> */}
</div>
</div>
);
} else {
return <div></div>;
}
}

View File

@@ -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);
}

View File

@@ -64,7 +64,7 @@ export default function DiscussCard({
if (index > 2) { if (index > 2) {
return ''; return '';
} }
return <div className={styles.tag}>{tag}</div>; return <div key={tag} className={styles.tag}>{tag}</div>;
})} })}
</div> </div>
</div> </div>

View File

@@ -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 (
<div>
<div className={styles.imageContainer}>
<div className={styles.blogHeaderText}>
SailPoint Developer Marketplace
</div>
</div >
</div>
);
}

View File

@@ -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;
}

View File

@@ -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 = (
<div></div>
);
if (post.tags.includes("sailpoint-authored")) {
badge = (
<img
className={styles.cardBadge}
src={useBaseUrl('/icons/SailPoint-LogoIcon-RGB-Color.svg')}></img>
);
}
return (
<div onClick={(e) => setFilters(e)}>
<div className={styles.card} >
<div className={styles.cardData}>
<img className={styles.cardEye} src={useBaseUrl('/blog/eye-regular.svg')}></img>
<div className={styles.cardCommentText}>{post.views}</div>
<img className={styles.cardComment} src={useBaseUrl('/blog/comment-light.svg')}></img>
<div className={styles.cardCommentText}>{post.replies}</div>
</div>
<div className={styles.cardUser}>
<img className={styles.cardFace} src={useBaseUrl(post.creatorImage)}></img>
<div className={styles.cardName}>{post.name}</div>
</div>
{badge}
<div className={styles.cardText}>
<img className={styles.cardImage} src={useBaseUrl(post.image)}></img>
<div className={styles.cardTitle}>{post.title}</div>
<div className={styles.tags}>
{post.tags?.map((tag, index) => {
if (index > 2) {
return '';
}
return <div key={tag} className={styles.tag}>{tag}</div>;
})}
</div>
<ReactMarkdown className={styles.cardBody}>{post.excerpt}</ReactMarkdown>
</div>
</div>
</div>
);
}

View File

@@ -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;
}

View File

@@ -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('</div>', 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 (
<div className={styles.detailContainer}>
<div className={styles.detailHeader}>
<div className={styles.detailTitle}>{details.title}</div>
<img
className={styles.detailImage}
src={useBaseUrl(details.image)}></img>
</div>
<Tabs className={styles.detailTabs}>
<TabList>
<Tab>Details</Tab>
<Tab>Requirements</Tab>
<Tab>Support</Tab>
</TabList>
<TabPanel>
<ReactMarkdown className={styles.detailTabContent}>
{getDivText(rawPost, 'details')}
</ReactMarkdown>
<button
className={styles.modalButton}
onClick={async () => {
goToLink(details.link);
}}>
<div className={styles.modalButtonText}>
<img
className={styles.buttonImage}
src={useBaseUrl('/icons/discourse.svg')}></img>
See More
</div>
</button>
<button
className={styles.modalButton}
onClick={async () => {
console.log(getDivText(rawPost,'github'))
const regex = /\(([^)]+)\)/;
goToLink(getDivText(rawPost,'github').match(regex)[1]);
}}>
<div className={styles.modalButtonText}>
<img
className={styles.buttonImage}
src={useBaseUrl('/icons/github.svg')}></img>
GitHub
</div>
</button>
</TabPanel>
<TabPanel>
<ReactMarkdown className={styles.detailTabContent}>
{getDivText(rawPost, 'requirements')}
</ReactMarkdown>
</TabPanel>
<TabPanel>
<ReactMarkdown className={styles.detailTabContent}>
{getDivText(rawPost, 'support')}
</ReactMarkdown>
</TabPanel>
</Tabs>
</div>
);
} else {
return (
<BounceLoader
className={styles.spinnerCenter}
color={'#0033a1'}
loading={true}
size={150}
aria-label="Loading Spinner"
data-testid="loader"
/>
);
}
}

View File

@@ -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;
}

View File

@@ -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 (
<div className={styles.center}>
<div className={styles.gridContainer}>
{cardData.map(function (a, index) {
return (
<MarketplaceCard
post={a}
key={index + a.link}
openDialogFunc={openDialog}></MarketplaceCard>
);
})}
</div>
<Modal
isOpen={detailsOpen}
className={styles.modal}
contentLabel="Details">
<div>
<div>
<MarketplaceCardDetail
details={details.data}
rawPost={details.raw}></MarketplaceCardDetail>
</div>
<img
className={styles.cardExit}
src={xImage}
onClick={async () => {
setDetailsOpen(false);
}}></img>
</div>
</Modal>
</div>
);
} else if (loadingCards) {
return (
<BounceLoader
className={styles.spinnerCenter}
color={'#0033a1'}
loading={true}
size={150}
aria-label="Loading Spinner"
data-testid="loader"
/>
);
} else {
return (
<div className={styles.noFound}>
{' '}
No Marketplace Item Found with the Given Search Criteria
</div>
);
}
}
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('&hellip;', '');
}
} else {
return '';
}
}

View File

@@ -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;
}

View File

@@ -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 <div key={text} onClick={(e) => setFilters(e, text)} className={activeClass + ' ' + styles.tag}>{text}</div>
}

View File

@@ -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);
}

View File

@@ -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 (
<div className={styles.sidebar}>
<div className={styles.tagHeader}>Items by Product</div>
<div className={styles.tagContainer}>
{tagProductData.map(function(a, index){
return <MarketplaceSidebarButton key={a} text={a} filterCallback={filterCallback}></MarketplaceSidebarButton>
})}
</div>
<div className={styles.tagHeader}>Items by Identity Governance</div>
<div className={styles.tagContainer}>
{tagTechnologyData.map(function(a, index){
return <div key={'div' + a} className={index > 10 && filterTags ? styles.hidden : ''} > <MarketplaceSidebarButton key={a} text={a} filterCallback={filterCallback}></MarketplaceSidebarButton></div>
})}
</div>
<div className={styles.seeAll} onClick={(e) => toggleSeeAll()}>
{filterText}
{/* <img className={styles.caretDown} src={useBaseUrl('/blog/caret-down-thin.svg')}></img> */}
</div>
</div>
);
} else {
return <div></div>;
}
}

View File

@@ -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);
}

View File

@@ -244,6 +244,7 @@
--dev-text-color-secondary: #0033a1; --dev-text-color-secondary: #0033a1;
--dev-secondary-text: #415364; --dev-secondary-text: #415364;
--dev-tag-highlight: #eaeef1;
--text-on-primary: #ffffff; --text-on-primary: #ffffff;
@@ -286,6 +287,7 @@
--dev-card-shadow: 0 4px 5px rgba(0, 0, 0, 0.2); --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-card-selected: 0 5px 5px rgba(107, 107, 107, 0.2);
--dev-secondary-text: #dae1e9; --dev-secondary-text: #dae1e9;
--dev-tag-highlight: #00000075;
/*main hero card*/ /*main hero card*/
--main-hero-card-background: #202122; --main-hero-card-background: #202122;

41
src/pages/blog.js Normal file
View File

@@ -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 (
<Layout description="The SailPoint Developer Community has everything you need to build, extend, and automate scalable identity solutions.">
<main>
<BlogBanner />
<div className={styles.blogContainer}>
<div className={styles.blogSidbarContainer}><BlogSidebar filterCallback={handleClick}/></div>
<div className={styles.blogCardContainer}><BlogCards filterCallback={filteredProduct}/></div>
</div>
</main>
</Layout>
);
}

17
src/pages/blog.module.css Normal file
View File

@@ -0,0 +1,17 @@
.blogContainer {
display: flex;
}
.blogSidbarContainer {
flex: 5%;
}
@media only screen and (max-width: 870px) {
.blogSidbarContainer {
display: none;
}
}
.blogCardContainer {
flex: 95%;
}

41
src/pages/marketplace.js Normal file
View File

@@ -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 (
<Layout description="The SailPoint Developer Community has everything you need to build, extend, and automate scalable identity solutions.">
<main>
<MarketplaceBanner />
<div className={styles.blogContainer}>
<div className={styles.blogSidbarContainer}><MarketplaceSidebar filterCallback={handleClick}/></div>
<div className={styles.blogCardContainer}><MarketplaceCards filterCallback={filteredProduct}/></div>
</div>
</main>
</Layout>
);
}

View File

@@ -0,0 +1,17 @@
.blogContainer {
display: flex;
}
.blogSidbarContainer {
flex: 5%;
}
@media only screen and (max-width: 870px) {
.blogSidbarContainer {
display: none;
}
}
.blogCardContainer {
flex: 95%;
}

View File

@@ -8,3 +8,81 @@ export async function getTopPosts() {
return []; 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 [];
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M144.6 361.4c4 4.2 9.6 6.6 15.4 6.6s11.4-2.4 15.4-6.6L300.5 229.3c2.2-2.3 3.5-5.4 3.5-8.7c0-7-5.6-12.6-12.6-12.6H28.6c-7 0-12.6 5.6-12.6 12.6c0 3.2 1.2 6.3 3.5 8.7L144.6 361.4zm-11.6 11L7.8 240.3C2.8 235 0 227.9 0 220.6C0 204.8 12.8 192 28.6 192H291.4c15.8 0 28.6 12.8 28.6 28.6c0 7.3-2.8 14.4-7.8 19.7L187 372.4c-7 7.4-16.8 11.6-27 11.6s-20-4.2-27-11.6z"/></svg>

After

Width:  |  Height:  |  Size: 602 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M480 256A224 224 0 1 1 32 256a224 224 0 1 1 448 0zM0 256a256 256 0 1 0 512 0A256 256 0 1 0 0 256zM240 112V256c0 5.3 2.7 10.3 7.1 13.3l96 64c7.4 4.9 17.3 2.9 22.2-4.4s2.9-17.3-4.4-22.2L272 247.4V112c0-8.8-7.2-16-16-16s-16 7.2-16 16z"/></svg>

After

Width:  |  Height:  |  Size: 479 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M256 64C125.8 64 32 148.6 32 240c0 37.1 15.5 70.6 40 100c5.2 6.3 8.4 14.8 7.4 23.9c-3.1 27-11.4 52.5-25.7 76.3c-.5 .9-1.1 1.8-1.6 2.6c11.1-2.9 22.2-7 32.7-11.5L91.2 446l-6.4-14.7c17-7.4 33-16.7 48.4-27.4c8.5-5.9 19.4-7.5 29.2-4.2C193 410.1 224 416 256 416c130.2 0 224-84.6 224-176s-93.8-176-224-176zM0 240C0 125.2 114.5 32 256 32s256 93.2 256 208s-114.5 208-256 208c-36 0-70.5-6.7-103.8-17.9c-.2-.1-.5 0-.7 .1c-16.9 11.7-34.7 22.1-53.9 30.5C73.6 471.1 44.7 480 16 480c-6.5 0-12.3-3.9-14.8-9.8s-1.1-12.8 3.4-17.4c8.1-8.2 15.2-18.2 21.7-29c11.7-19.6 18.7-40.6 21.3-63.1c0 0-.1-.1-.1-.2C19.6 327.1 0 286.6 0 240z"/></svg>

After

Width:  |  Height:  |  Size: 857 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M123.6 391.3c12.9-9.4 29.6-11.8 44.6-6.4c26.5 9.6 56.2 15.1 87.8 15.1c124.7 0 208-80.5 208-160s-83.3-160-208-160S48 160.5 48 240c0 32 12.4 62.8 35.7 89.2c8.6 9.7 12.8 22.5 11.8 35.5c-1.4 18.1-5.7 34.7-11.3 49.4c17-7.9 31.1-16.7 39.4-22.7zM21.2 431.9c1.8-2.7 3.5-5.4 5.1-8.1c10-16.6 19.5-38.4 21.4-62.9C17.7 326.8 0 285.1 0 240C0 125.1 114.6 32 256 32s256 93.1 256 208s-114.6 208-256 208c-37.1 0-72.3-6.4-104.1-17.9c-11.9 8.7-31.3 20.6-54.3 30.6c-15.1 6.6-32.3 12.6-50.1 16.1c-.8 .2-1.6 .3-2.4 .5c-4.4 .8-8.7 1.5-13.2 1.9c-.2 0-.5 .1-.7 .1c-5.1 .5-10.2 .8-15.3 .8c-6.5 0-12.3-3.9-14.8-9.9c-2.5-6-1.1-12.8 3.4-17.4c4.1-4.2 7.8-8.7 11.3-13.5c1.7-2.3 3.3-4.6 4.8-6.9c.1-.2 .2-.3 .3-.5z"/></svg>

After

Width:  |  Height:  |  Size: 929 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M288 80c-65.2 0-118.8 29.6-159.9 67.7C89.6 183.5 63 226 49.4 256c13.6 30 40.2 72.5 78.6 108.3C169.2 402.4 222.8 432 288 432s118.8-29.6 159.9-67.7C486.4 328.5 513 286 526.6 256c-13.6-30-40.2-72.5-78.6-108.3C406.8 109.6 353.2 80 288 80zM95.4 112.6C142.5 68.8 207.2 32 288 32s145.5 36.8 192.6 80.6c46.8 43.5 78.1 95.4 93 131.1c3.3 7.9 3.3 16.7 0 24.6c-14.9 35.7-46.2 87.7-93 131.1C433.5 443.2 368.8 480 288 480s-145.5-36.8-192.6-80.6C48.6 356 17.3 304 2.5 268.3c-3.3-7.9-3.3-16.7 0-24.6C17.3 208 48.6 156 95.4 112.6zM288 336c44.2 0 80-35.8 80-80s-35.8-80-80-80c-.7 0-1.3 0-2 0c1.3 5.1 2 10.5 2 16c0 35.3-28.7 64-64 64c-5.5 0-10.9-.7-16-2c0 .7 0 1.3 0 2c0 44.2 35.8 80 80 80zm0-208a128 128 0 1 1 0 256 128 128 0 1 1 0-256z"/></svg>

After

Width:  |  Height:  |  Size: 966 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M128 32H256c17.7 0 32 14.3 32 32V89.7C259.8 73.3 227 64 192 64s-67.8 9.3-96 25.7V64c0-17.7 14.3-32 32-32zM64 112h1C25.1 147.2 0 198.7 0 256s25.1 108.8 65 144H64v48c0 35.3 28.7 64 64 64l128 0c35.3 0 64-28.7 64-64V400h-1c39.9-35.2 65-86.7 65-144s-25.1-108.8-65-144h1V64c0-35.3-28.7-64-64-64H128C92.7 0 64 28.7 64 64v48zM288 422.3V448c0 17.7-14.3 32-32 32l-128 0c-17.7 0-32-14.3-32-32V422.3c28.2 16.3 61 25.7 96 25.7s67.8-9.4 96-25.7zM32 256a160 160 0 1 1 320 0A160 160 0 1 1 32 256zm176-80c0-8.8-7.2-16-16-16s-16 7.2-16 16v80c0 5.3 2.7 10.3 7.1 13.3l48 32c7.4 4.9 17.3 2.9 22.2-4.4s2.9-17.3-4.4-22.2L208 247.4V176z"/></svg>

After

Width:  |  Height:  |  Size: 860 B

View File

@@ -0,0 +1,466 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_extend "http://ns.adobe.com/Extensibility/1.0/">
<!ENTITY ns_ai "http://ns.adobe.com/AdobeIllustrator/10.0/">
<!ENTITY ns_graphs "http://ns.adobe.com/Graphs/1.0/">
<!ENTITY ns_vars "http://ns.adobe.com/Variables/1.0/">
<!ENTITY ns_imrep "http://ns.adobe.com/ImageReplacement/1.0/">
<!ENTITY ns_sfw "http://ns.adobe.com/SaveForWeb/1.0/">
<!ENTITY ns_custom "http://ns.adobe.com/GenericCustomNamespace/1.0/">
<!ENTITY ns_adobe_xpath "http://ns.adobe.com/XPath/1.0/">
]>
<svg version="1.1" id="Layer_1" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 709.6 703.55"
style="enable-background:new 0 0 709.6 703.55;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0033A1;}
.st1{fill:#CC27B0;}
.st2{fill:#0071CE;}
.st3{fill:#E17FD2;}
</style>
<switch>
<foreignObject requiredExtensions="&ns_ai;" x="0" y="0" width="1" height="1">
<i:aipgfRef xlink:href="#adobe_illustrator_pgf">
</i:aipgfRef>
</foreignObject>
<g i:extraneous="self">
<g id="Layer_2_00000013178645319098401590000002275302964206807725_">
<polygon class="st0" points="379.75,172.71 423.83,435.76 168.74,435.76 "/>
<polygon class="st1" points="379.75,172.71 535.98,435.76 423.83,435.76 "/>
<polygon class="st2" points="168.74,435.76 423.83,435.76 439.77,530.83 "/>
<polygon class="st3" points="423.83,435.76 535.98,435.76 439.77,530.83 "/>
</g>
</g>
</switch>
<i:aipgf id="adobe_illustrator_pgf" i:pgfEncoding="zstd/base64" i:pgfVersion="24">
<![CDATA[
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
]]>
<![CDATA[
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
]]>
<![CDATA[
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
]]>
<![CDATA[
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
]]>
<![CDATA[
YE8pBGxVr22vXoP1QMH0H4TaDVXvH4BMFaC9nrHlV71n/B2OeXdJOMCfHKnq/a2p4FMMulZLuXr/
qfb/M+VVvbQz+Nf7UlogYLx6TTdXpU5AR6l6NzIir95a3DZQMfSy+vvzBl315sP7V68WZzKUqrfe
OmyuM22NTIWqNzVPwaB+/eodNlcvQYwrdH2GGbregmrDYyg/ZvcQOiwQzRFG49d3ttpL1/3ZvfdG
zGTb63D/IywrHRlLU9cZ6Prfm6Ak1v+hkE0cy41tEaiQNb1+cHgr0UuT8dmFSw+LOgDKzOJNuHXw
A2iF7ENdSTRWzKZE4fWLTg1H0aPMWnzHDGE3+vfI76sFo25KLkZcOJqSypaXxS5NXjptUi9/ooaK
8jIUaUewjNdz+PIdz+J1bXOlcB0xmAUUxAmWKirqNPZC6bKN+Z/82eJAN+wLMD+4uHzpacu6zW6m
zSn3NWO/NAXsBv41YABs9ou+9sGTnGohNr2vfs6J+qu7HQAumInr8kbRCklgmQ8yqAa4hL6ShNec
1ehemHynLOiQeAPEXJJmarVysKa4rNY6xtIx7QjeO901jiORzyqBeFcR3S3yEiFcwip0m1nTM9Ad
OJWBJMn1LENf2it0kiMhk6bMPv3ddc010eQY08A3a6lsRhFemkHxbqH0Y+W+mHVBz93Y9tTf3V3s
XPvhrlC826u80fTKeXCcZo9f7F+L8uCnLYDDaxsEMzQ0w+fdrfJePYYIua2yincZCWGmNmMiemgH
CBsK491aswCsfnfJavTnvNHH7+5z6G7uyWL5laFFbVa77pFVMEZMgPJleyjgEdX1m31p7cjg0mgR
9KcCrqx75fhkE4WbIaXp4uipQtXmfL/DUYAnPhjg2uKaiTKgmfhI9QSJgJul6vBo5joqCfToTYBP
hpCb6uKeQRFJxfLWGkR1Q129uYu/XcvvULOgFWhNuUhQTf0No2NqeiG+FdN9CT1BM3V4lwMoOa7o
FBJ92+bEEL8S/FpG411a/AOmyXgVd4WmjMZi+IYbe4HksMvhKbMlSupq/Ektg82kjfB6P2Rk8t9A
Yc05ibzvEqS2rqVW8rdf9rzmiM0/vWwVEnxW5+XiRq95PvWqj2X0kox8mhjSGDL1GDoFH1/nCzpY
LJT8hoPbvW7C9bOZ04ZumOPcx1h4XvpSbDWH8bqu9fHwr5Na5MPOrBpPtt5duSGZTfy1yfKyfWxZ
j40TCeCi4sYbCVl2RxdYcPoXO994Z3V0Y1R8AIuX6HLlXbNpRgyqygQD3p3bLtN3ezL7kFDyh92t
TD31MTHhLpZdtUq9MUhlbSuA8j93Puz0jVhHrj3Tb9OCU6xlM5eoG9KDWop9DRu4YNdmz+3vX3io
6Jf+ybA0/Q9lc9QQEtEZMz4tod1WyK7xCyqEM7IeZRIRLZLIJk0/ez3nCzZBOThYi5W3+YgCCygN
GLzBhX6MctBJ4c3U6FaJwSN7GY62TwbxbD4MJTmH/Nq5h5OESGlL6FcUjqwz69ioHLL/sl78yGwk
Dl2mvCShFRKrsjGJw2PMssoQkwEiyxr6dDX6OPcEopnSFFwFVC3TnH0G4cfF1baA3KNrb0jtiA/X
kt94kTy6o2DLXS2Z06/NVdewD3a5qJC4XLqGUi+RI4kOlv9bqMYHitEisovok9a36DI4JCwnbsrC
zqA55Ws+IKfNV/65Olrw6MMOkTVF18rtHl1LhSDxjup437U8ltk2TfsL4Duf69gSr+Xfa4n3Duxg
0BwUh7jZcu9Rv+NOiQgtirWL4sFWcHvFZenTSH7vlugPsVT03c9Z3hsgznbv7yLzg3KS9oqQrbi3
y5CsdQU/KS6kPH0g/4cYmr694tBkQRShlGhIhvAV24vPe8jCRtPwA3IyfoisFbnVeZkkGr8hliVS
Pefqgm9kqyaA72dK5qWvCZ2ZnDkgAIKCKXHibQgplCnltwGFwAHlTpqY+P5L28hqHLIHQ9+rFMlE
PJxfEiKZLvYaC3h31OQdew3cxcDzQ8tZnuf2vH2sPRy9w6UjpIgEoJQoc+KyYTO/m+S6KCcA5cvj
io4Qu7nkiq5PaMh+WB3sr33Zan2znnrb4/OzVWioiFBh5JNh/sCSWi6CMadI+2X15TzZRLTLm5uc
dKxsSJMC4fHi8a0Fn7yfHqFwwdyi8f57KJ6VRYLlEGtLZ/5ixkuemlk5UNsrRLrhI97tRxSZG3ns
75vHr71qZueHCasFWNAyUc90o9YNOwHTtdkLH5cKmhimm0IZtv6HE4nGe2e6MF4kRild54uvYrqJ
RZU5tkqNTekiMirwMt3sx3R7FEwiVDPdfkZXGlbJ2Uq3rBR6mOmmJQ6LsV/zCSQw3ds1qHKyot4X
JcxkxJ2k63HyMYm+Zeb0dCfTMMul8uFRYF7NrtUrCvlWBNtkPYnUB18baWEt39mbyNmHNflbGQuY
ADx9BFiEmkwXADhyB45/Dq/KKMo9y+gMnIXReyqylk6R+f8wtq4MRtZdXcbIqjm1pS9V+lut7WSa
n0GNloIqI/2aHs6UMCv7cuHGSMo1LMZXYcybnn0rg1FWuiRZWkd5J+5hqjJsMv0gfQ17Lv529VLz
llMaUNV8xQIvHq1n1FPmnce6cpFsxPwIVP/KTEakzQCWfDQk3ARX1q89CS+12LoY6ALBJV33MFF2
72gnSRTlklai3m4q3vWL2Qhl6e+FjxIoGEqsJJAXELIG5FZUAvHo/C8YHdZXsyzQdYrI3orFCKVG
AP6RBGbbHrTaIZ4pTidyi0ZZaa3pnsnTdORzEbCX+sN3eJhTXoJ18C0fM7AzfMrTL+phXZ5Si0Eb
d5sox93AHJhrteuxVT8e4ZiezKtOSLjQWlIseJ/kWDtvRc3tG861XiGqqK3qe832zyEpCMMNjvOe
QfCNnWVdOANpcEZyqtZAoGaWCczN4TDnpoHKEzwH8/0Syu3FklCiP9/OEC4TWIIvE53GBBGqvV/i
ffVjitEj+peGmH725SHMzJ2XWMaOjjf9JXs0QzRiMD45ob4WfjKwVUgKFg5eJFNn+R4lw2h71dlj
EUrb/pqEY6CvsEDE2VtRxu2rhy/QrnhXWAszzOH0XjbnfP0mv0wyQoruCWVIfT0SJPnM2ZNKSToS
+j2zV2IqXIbZm9+55r8MYZkW+iP4GMx/j/RqmdGqKMBGm8lsstkDbqzJipZlBVm55CnEP7aLn2DG
T7W4pOIn9Ok4E34ZrWo8ACqr4GCPxyEtzqn252J8vAFBSzi+XSUiylOBfwssy24x62ZOizxbfNw6
IQH+Prs/BnAIqmjwFWIwn9T4ke7+w4D+dorYMq52bANg47nPKz6cHWAIYs2MVA4uhQjYvAsxjsgY
WvlTX7Pzbi7qLJ3JATOeVfdZm2iW1IZWKG7J55jTHZkzbmkW9/I5fqyieL9PzSQ1rgKHyhQdifiB
4pLNjKsBD8ZH2m5KeVEVucCI2MZvcvYZZLG4j79kAywcnKIIscqVSCF3MngTsy7pWS5NTMdBbZpl
/A3+Ksp8QzqEqqPHVQHJ1XZJfP5wwN3+jv89lx9l7HPhyBz8nMbHfCK4va57SJsZYPdHT8lCTi6v
Qf91YA7ybRHId8ZXn5hTPSMoX7J5HbVwM82Q81fBbmK6tT/W7fv/WrtZUekPqMEPhqixmsZAojGJ
t8Ri3vYxlizVEXty6JixQDQMs26FveUbNnFWWEpWsOqAi2sn97vEkR1/dvsSXj5wCN37TNkDEIm8
9Tl5XdrpR3C7Ij0Wq98EOwO4CM6BxC3iTHj0pgAicXoEu7zYW1DeM25XArIFY8xeE0MiPeahprpf
S3Eq7CIJTZuJOYLfVQUMhrzt1QXq1B5Lup6f3ggIDfWorrGUNNvTvtcVPdwGMczTWdMVbSG4rhE+
Rt9PiYaHSPDpenL8f1ds6OkWUO9kJ+dGvNvDNPwANsUAd1gsg0exUuYRkG1qcV2KeJZ1eKUhobjC
qwMcMbXOn2HQVGoC6EW8WUC5NLa52cP0i/jCkgdG62HCyaqJY+irhchm3a5uN3d/HB+jxo91EsM3
PupOhfzO1EIevtTU4zOsL3fhI65SzvE9Wj0fNmLezPJIEOLwMM0RhCp2swwQnIPljHCdj4v39gg4
6N/q1hmE4DDj8AJHgxAPLs/OpLnHC0CZUN0nDJnVJvlCBMLAJv50dk7bBDVIxlJxvyr/QQa0aqOW
osbdgP9pReuw6WhNYDpJRaSNhB2+ExSM7sVai19l7hKUXlQAlLOXmHVDGy/qw0bG6eKiTGGgqZ6Y
/sfuy5mXfdXwTA6Nv690GnJKh6sp7wr2kY8egak9PGajGIarOeh/AdVEZI35Nb9eNXcVXsLSuOGb
Uu5AGVzizRPVgAs9ggvg5isH15vGBZz0fV8wqr33FPyWRchZZxCtLW1rPzQdjTer59szRQy2cleN
uwaBKr1BGDhFjnASJwT7TrXfW2VTR6Kj8Cif5xI7LKYmbArbxaXDiYFPuir5zpMFy9PvxMMXwC6L
fJAUq7DVMQKAivZzWXvNSP0qOTJv5qp5loU5XsUV6IkhY7D/QFfrTQe14hpLyZZ7DgQeYyUr472l
lvaKhdSMhvIDUuRnZbmWfO/jHQIcxzfwK2+BvfEWdSn2S+7iYfaFzBezDWO+q8Bcy+Tcyr6p93tA
zoapzZRjYcibrQL4xD3qg5fBoTBAIuFKUFRYvs2cntNMqzEe+Ecs7UDtrvzRae+4mrnyxmBNuDYh
Tqs/GWzMgkDRiJnAHvkpHBAPfaR0oNKw4XSK69rEF6TSX/N3P978rxjS7iKHQviJvCywC5rLWRxM
vHTseX/1F30UnPePUxTJlm8tlsVRWASIiWvt5A8/wUVJ+HKbyjpgbCo81aRs1qqo9TysM0p6YV4o
mB3NBpcFH803WBgXYWgss4L8x2HuFPdRBq7vxuU3LSPBFAH5yQVtE1UOCEs0D/TrLbaVQX25cjfm
v1vw35B4TKxNtx/TP//psZbTVMP/d1IOMhYNAlbk+VPQyjSNqOli1EMKklJJILN8V9ISbOD9+nGo
hQT9vVDQHgPkykXDyqywq4Q3ITXaU1BflIj6BwiDKUY3LYtb/7/0XuMDZTWgQbNYliRwH4wIqXlN
vjyEqkAyBh966EEJIpcBCNtArNA1r46UIUv88MtyDfnNGBifDHo43C/R+ovMLTL5fz/2cAv4xMaN
ZLRcL5YiN7UdDPD2Wh7Zsz+6sfvGK68fXgUOynU1Nmv7HOZ2NHJNo4VMbapK4TJKpazNWe0m9AE/
7OSMLiGQv4ubA/yHlX4gXSte3O84hxzGBPJuxyFobJ3o9U01wnPiU6ccuxGhvMess7zMe4muxleC
QnFze4XCeILCp443sI7O86cpO/hGJdkaNoiKBT0097wfiEAgASDax6B9bob9JZhr3BUrMbItgUZZ
qd9QBntyR0AwiYU6NaGYAKmwNWrM/J/9T/Oyo4ZDLrfEAWtZEmb2lCkb2t8/do/ldw==
]]>
<![CDATA[
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
jvyOGtKLkonVP53m+h2CKf2zmWOfowliBw==
]]>
</i:aipgf>
</svg>

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M256 48a208 208 0 1 1 0 416 208 208 0 1 1 0-416zm0 464A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM175 175c-9.4 9.4-9.4 24.6 0 33.9l47 47-47 47c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l47-47 47 47c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-47-47 47-47c9.4-9.4 9.4-24.6 0-33.9s-24.6-9.4-33.9 0l-47 47-47-47c-9.4-9.4-24.6-9.4-33.9 0z"/></svg>

After

Width:  |  Height:  |  Size: 574 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M225.9 32C103.3 32 0 130.5 0 252.1 0 256 .1 480 .1 480l225.8-.2c122.7 0 222.1-102.3 222.1-223.9C448 134.3 348.6 32 225.9 32zM224 384c-19.4 0-37.9-4.3-54.4-12.1L88.5 392l22.9-75c-9.8-18.1-15.4-38.9-15.4-61 0-70.7 57.3-128 128-128s128 57.3 128 128-57.3 128-128 128z"/></svg>

After

Width:  |  Height:  |  Size: 511 B

1
static/icons/github.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB