mirror of https://github.com/lukechilds/docs.git
Alexander Graebe
4 years ago
committed by
Alexander Graebe
7 changed files with 21 additions and 247 deletions
@ -1,66 +0,0 @@ |
|||||
import { convertRemoteDataToMDX } from '@common/data/mdx'; |
|
||||
import TurndownService from 'turndown'; |
|
||||
import { slugify } from '@common/utils'; |
|
||||
import { getBetterNames } from '@common/utils/faqs'; |
|
||||
|
|
||||
const fetchSections = async () => { |
|
||||
const res = await fetch('https://blockstack.zendesk.com/api/v2/help_center/en-us/sections.json'); |
|
||||
const { sections } = await res.json(); |
|
||||
return sections; |
|
||||
}; |
|
||||
const fetchArticles = async (id: number) => { |
|
||||
const res = await fetch( |
|
||||
`https://blockstack.zendesk.com/api/v2/help_center/en-us/sections/${id}/articles.json?per_page=100` |
|
||||
); |
|
||||
const { articles } = await res.json(); |
|
||||
return articles; |
|
||||
}; |
|
||||
|
|
||||
// This function gets called at build time
|
|
||||
export async function getStaticPaths() { |
|
||||
const sections = await fetchSections(); |
|
||||
const paths = sections.map(section => ({ |
|
||||
params: { slug: slugify(getBetterNames(section.id).title) }, |
|
||||
})); |
|
||||
|
|
||||
return { paths, fallback: false }; |
|
||||
} |
|
||||
|
|
||||
const getSectionBySlug = (sections, slug) => |
|
||||
sections.find(s => { |
|
||||
const { title } = getBetterNames(s.id); |
|
||||
const _slug = slugify(title); |
|
||||
return _slug === slug; |
|
||||
}); |
|
||||
|
|
||||
export async function getStaticProps(context) { |
|
||||
const sections = await fetchSections(); |
|
||||
let articles = []; |
|
||||
|
|
||||
if (context?.params?.slug) { |
|
||||
const section = getSectionBySlug(sections, context.params.slug); |
|
||||
const _articles = await fetchArticles(section.id); |
|
||||
const turndownService = new TurndownService(); |
|
||||
// we convert html to markdown so we can process it with remark/rehype,
|
|
||||
// eg external links open in new window
|
|
||||
const md = _articles.map(faq => ({ |
|
||||
...faq, |
|
||||
body: turndownService.turndown(faq.body), |
|
||||
})); |
|
||||
// convert it to MDX with next-mdx-remote
|
|
||||
const body = await convertRemoteDataToMDX(md, 'body'); |
|
||||
articles = _articles.map((faq, index) => ({ |
|
||||
...faq, |
|
||||
body: body[index], |
|
||||
})); |
|
||||
} |
|
||||
|
|
||||
return { |
|
||||
props: { |
|
||||
sections, |
|
||||
articles, |
|
||||
...context, |
|
||||
}, |
|
||||
revalidate: 60 * 60 * 12, // 12 hours
|
|
||||
}; |
|
||||
} |
|
@ -1,61 +0,0 @@ |
|||||
import React from 'react'; |
|
||||
import { Box, space, color, Grid } from '@stacks/ui'; |
|
||||
import { Text } from '@components/typography'; |
|
||||
import { slugify } from '@common/utils'; |
|
||||
import { getCapsizeStyles, getHeadingStyles } from '@components/mdx/typography'; |
|
||||
import { HoverImage } from '@components/hover-image'; |
|
||||
import { useTouchable } from '@common/hooks/use-touchable'; |
|
||||
import Link from 'next/link'; |
|
||||
import { getBetterNames } from '@common/utils/faqs'; |
|
||||
|
|
||||
const FloatingLink = ({ href, ...props }: any) => ( |
|
||||
<Link href={href} {...props} passHref> |
|
||||
<Box as="a" position="absolute" size="100%" zIndex={999} left={0} top={0} /> |
|
||||
</Link> |
|
||||
); |
|
||||
const SectionCard = ({ section }) => { |
|
||||
const { hover, active, bind } = useTouchable({ |
|
||||
behavior: 'button', |
|
||||
}); |
|
||||
const { title, description, img } = getBetterNames(section.id); |
|
||||
return ( |
|
||||
<Box |
|
||||
color={color('text-title')} |
|
||||
_hover={{ cursor: 'pointer', color: color('accent') }} |
|
||||
position="relative" |
|
||||
{...bind} |
|
||||
> |
|
||||
<FloatingLink href="/references/faqs/[slug]" as={`/references/faqs/${slugify(title)}`} /> |
|
||||
<HoverImage isHovered={hover || active} src={img} /> |
|
||||
<Box> |
|
||||
<Text color="currentColor" {...getHeadingStyles('h3')}> |
|
||||
{title} |
|
||||
</Text> |
|
||||
<Box> |
|
||||
<Text |
|
||||
display={'block'} |
|
||||
color={color('text-body')} |
|
||||
mt={space('base-loose')} |
|
||||
{...getCapsizeStyles(16, 26)} |
|
||||
> |
|
||||
{description} |
|
||||
</Text> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Box> |
|
||||
); |
|
||||
}; |
|
||||
export const FAQs = React.memo(({ articles, sections }: any) => { |
|
||||
return ( |
|
||||
<Grid |
|
||||
gridTemplateColumns={['repeat(1, 1fr)', 'repeat(2, 1fr)', 'repeat(2, 1fr)', 'repeat(2, 1fr)']} |
|
||||
gridColumnGap={space('extra-loose')} |
|
||||
gridRowGap="64px" |
|
||||
px={['extra-loose', 'extra-loose', 0, 0]} |
|
||||
> |
|
||||
{sections.map(section => { |
|
||||
return <SectionCard key={section.id} section={section} />; |
|
||||
})} |
|
||||
</Grid> |
|
||||
); |
|
||||
}); |
|
@ -1,10 +1,24 @@ |
|||||
--- |
--- |
||||
title: FAQs |
title: FAQs |
||||
description: A knowledge base of question and answers related to the Stacks ecosystem. |
description: Find answers related to the Stacks ecosystem. |
||||
duration: '' |
|
||||
--- |
--- |
||||
|
|
||||
import { FAQs } from '@components/faq' |
## General Information |
||||
export { getStaticProps } from '@common/data/faq' |
|
||||
|
|
||||
<FAQs sections={props.sections} /> |
Learn more about the user-owned internet on Bitcoin and the Stacks ecosystem on [stacks.co](https://stacks.co). |
||||
|
|
||||
|
## Apps and Smart Contracts |
||||
|
|
||||
|
Developers, get started building apps and contracts on the [developer page at stacks.co](https://www.stacks.co/developers). |
||||
|
|
||||
|
## Stacks Network |
||||
|
|
||||
|
Learn more about the network behind the user-owned internet on Bitcoin in the [Understand Stacks chapter](https://docs.blockstack.org/understand-stacks/overview) in the docs. |
||||
|
|
||||
|
## Stacks Token |
||||
|
|
||||
|
Stacks fuel apps and smart contracts on Bitcoin. Learn more at [stackstoken.com](https://stackstoken.com/faq). |
||||
|
|
||||
|
## Stacks Wallet |
||||
|
|
||||
|
Download and find resources about the Stacks Wallet by Hiro at [hiro.so](https://www.hiro.so/wallet). |
||||
|
@ -1,109 +0,0 @@ |
|||||
import React from 'react'; |
|
||||
import { Components } from '@components/mdx'; |
|
||||
import { Box, Flex, ChevronIcon, space, color, Grid } from '@stacks/ui'; |
|
||||
import hydrate from 'next-mdx-remote/hydrate'; |
|
||||
import { Accordion, AccordionItem, AccordionButton, AccordionPanel } from '@reach/accordion'; |
|
||||
import { border } from '@common/utils'; |
|
||||
import { useRouter } from 'next/router'; |
|
||||
import { useActiveHeading } from '@common/hooks/use-active-heading'; |
|
||||
import { BackButton } from '@components/back-button'; |
|
||||
import Head from 'next/head'; |
|
||||
import { MDContents } from '@components/mdx/md-contents'; |
|
||||
export { getStaticProps, getStaticPaths } from '@common/data/faq'; |
|
||||
import { slugify, getSlug } from '@common/utils'; |
|
||||
import { PageTop } from '@components/page-top'; |
|
||||
import { getBetterNames } from '@common/utils/faqs'; |
|
||||
|
|
||||
const FAQItem = React.memo(({ faq, ...rest }: any) => { |
|
||||
const id = slugify(faq.title); |
|
||||
const { isActive } = useActiveHeading(id); |
|
||||
|
|
||||
return ( |
|
||||
<Components.section> |
|
||||
<Box as={AccordionItem} borderBottom={border()} {...rest}> |
|
||||
<Flex |
|
||||
as={AccordionButton} |
|
||||
_hover={{ color: color('accent') }} |
|
||||
{...{ |
|
||||
display: 'flex', |
|
||||
width: '100%', |
|
||||
outline: 'none', |
|
||||
bg: 'transparent', |
|
||||
border: '0', |
|
||||
alignItems: 'center', |
|
||||
justifyContent: 'space-between', |
|
||||
py: space('extra-loose'), |
|
||||
textAlign: 'left', |
|
||||
color: isActive ? color('accent') : color('text-title'), |
|
||||
_hover: { |
|
||||
cursor: 'pointer', |
|
||||
color: color('accent'), |
|
||||
}, |
|
||||
}} |
|
||||
> |
|
||||
<Components.h4 my="0px !important" id={id} color="currentColor"> |
|
||||
{faq.title} |
|
||||
</Components.h4> |
|
||||
<Box color={color('text-caption')} pl={space('base-loose')}> |
|
||||
<ChevronIcon direction="down" size="22px" /> |
|
||||
</Box> |
|
||||
</Flex> |
|
||||
<Box pb={space('extra-loose')} as={AccordionPanel}> |
|
||||
{hydrate(faq.body, { components: Components })} |
|
||||
</Box> |
|
||||
</Box> |
|
||||
</Components.section> |
|
||||
); |
|
||||
}); |
|
||||
|
|
||||
const FaqItems = ({ articles }) => { |
|
||||
const router = useRouter(); |
|
||||
const slug = getSlug(router.asPath); |
|
||||
const slugIndex = articles.findIndex(faq => slugify(faq.title) === slug); |
|
||||
const [index, setIndex] = React.useState(slugIndex !== -1 ? slugIndex : 0); |
|
||||
const handleIndexChange = (value: number) => { |
|
||||
setIndex(value); |
|
||||
}; |
|
||||
|
|
||||
return ( |
|
||||
<Box |
|
||||
pr={['extra-loose', 'extra-loose', 'base-loose', 'base-loose']} |
|
||||
pl={['extra-loose', 'extra-loose', '0', '0']} |
|
||||
> |
|
||||
<BackButton href="/references/faqs" mb={0} /> |
|
||||
<Accordion multiple collapsible defaultIndex={index} onChange={handleIndexChange}> |
|
||||
{articles |
|
||||
// @ts-ignore
|
|
||||
.sort((a, b) => new Date(a.created_at) - new Date(b.created_at)) |
|
||||
.map((faq, _index) => { |
|
||||
return <FAQItem faq={faq} key={_index} />; |
|
||||
})} |
|
||||
</Accordion> |
|
||||
</Box> |
|
||||
); |
|
||||
}; |
|
||||
|
|
||||
const FaqPage = props => { |
|
||||
const { articles, sections, params } = props; |
|
||||
|
|
||||
const section = sections.find(s => { |
|
||||
const { title } = getBetterNames(s.id); |
|
||||
const slug = slugify(title); |
|
||||
return slug === params.slug; |
|
||||
}); |
|
||||
|
|
||||
const { title, description } = getBetterNames(section.id); |
|
||||
return ( |
|
||||
<> |
|
||||
<Head> |
|
||||
<title>{title} | Stacks</title> |
|
||||
<meta name="description" content={description} /> |
|
||||
</Head> |
|
||||
<MDContents pageTop={() => <PageTop title={title} description={description} />} headings={[]}> |
|
||||
<FaqItems articles={articles} /> |
|
||||
</MDContents> |
|
||||
</> |
|
||||
); |
|
||||
}; |
|
||||
|
|
||||
export default FaqPage; |
|
Loading…
Reference in new issue