feat: feed feature, typescript, remove google, fix errs & warns
publish / Build-and-publish (push) Successful in 13m32s
publish / Build-and-publish (push) Successful in 13m32s
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
import React from 'react'
|
||||
import { FaRss } from 'react-icons/fa'
|
||||
import { Section } from '../shared'
|
||||
import { SolidButton } from '../shared/styledComponents'
|
||||
import { StyledHeading, StyledDesc, InputStyled, StyledAnchor } from './style'
|
||||
import { siteMetadata } from '../../../gatsby-config'
|
||||
|
||||
const rssFeedUrl = `${siteMetadata.siteUrl}rss.xml`
|
||||
|
||||
const anchorStyles = {
|
||||
color: '#888'
|
||||
@@ -13,42 +17,39 @@ const Footer = () => (
|
||||
<div className="col-md-3 me-md-5">
|
||||
<StyledHeading>Data Controller</StyledHeading>
|
||||
<StyledDesc>
|
||||
Data Controller is the product of a UK company with a singular focus on{' '}
|
||||
<StyledAnchor
|
||||
href="https://sasapps.io"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
SAS Web Apps
|
||||
</StyledAnchor>.
|
||||
Data Controller is the product of a UK company with a singular focus
|
||||
on <StyledAnchor href="https://sasapps.io">SAS Web Apps</StyledAnchor>
|
||||
.
|
||||
</StyledDesc>
|
||||
</div>
|
||||
<div className="col-md-3">
|
||||
<StyledHeading>Source Code</StyledHeading>
|
||||
<StyledDesc>
|
||||
All our source code can be found on our self-hosted{' '}
|
||||
<StyledAnchor
|
||||
href="https://git.datacontroller.io"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
Data Controller source is on our self-hosted{' '}
|
||||
<StyledAnchor href="https://git.datacontroller.io">
|
||||
Gitea Repository
|
||||
</StyledAnchor>
|
||||
.
|
||||
; the underlying SASjs framework is on{' '}
|
||||
<StyledAnchor href="https://github.com/sasjs">GitHub</StyledAnchor>.
|
||||
</StyledDesc>
|
||||
</div>
|
||||
<div className="col-md-3">
|
||||
<StyledHeading>Other Resources</StyledHeading>
|
||||
<StyledDesc>
|
||||
Leverage our underlying tech stack on{' '}
|
||||
<StyledAnchor
|
||||
href="https://github.com/sasjs"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
Github
|
||||
Connect on{' '}
|
||||
<StyledAnchor href={siteMetadata.social.linkedin}>
|
||||
LinkedIn
|
||||
</StyledAnchor>
|
||||
, read the{' '}
|
||||
<StyledAnchor href="https://docs.datacontroller.io">
|
||||
docs
|
||||
</StyledAnchor>
|
||||
, or subscribe to the{' '}
|
||||
<StyledAnchor href={rssFeedUrl}>
|
||||
<FaRss style={{ marginRight: '6px', verticalAlign: 'middle' }} />
|
||||
RSS feed
|
||||
</StyledAnchor>{' '}
|
||||
and build your own SAS Powered Web Apps.
|
||||
for updates.
|
||||
</StyledDesc>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React from 'react'
|
||||
import * as React from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import type { StyledAnchorProps } from '../../types'
|
||||
|
||||
export const StyledHeading = styled.h6`
|
||||
margin-bottom: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
@@ -33,7 +35,10 @@ const Anchor = styled.a`
|
||||
}
|
||||
`
|
||||
|
||||
export const StyledAnchor = ({ children, href }) => (
|
||||
export const StyledAnchor: React.FC<StyledAnchorProps> = ({
|
||||
children,
|
||||
href
|
||||
}) => (
|
||||
<Anchor href={href} target="_blank" rel="noopener">
|
||||
{children}
|
||||
</Anchor>
|
||||
@@ -1,21 +1,15 @@
|
||||
import React from 'react'
|
||||
import { PageProps, Link } from 'gatsby'
|
||||
|
||||
import styled from 'styled-components'
|
||||
import { Link } from 'gatsby'
|
||||
|
||||
import { Hero, HeroHeading, HeroDesc } from './style'
|
||||
import { BottomSectionArrow, OutlineButton } from '../shared/styledComponents'
|
||||
import { Container } from '../shared'
|
||||
|
||||
import { pathPrefix } from '../../../gatsby-config.js'
|
||||
import { pathPrefix } from '../../../gatsby-config'
|
||||
|
||||
type DataProps = {
|
||||
location: Location
|
||||
heading: string
|
||||
desc: string
|
||||
}
|
||||
import type { HeroSectionProps } from '../../types'
|
||||
|
||||
const HeroSection: React.FC<PageProps<DataProps>> = ({
|
||||
const HeroSection: React.FC<HeroSectionProps> = ({
|
||||
location,
|
||||
heading,
|
||||
desc
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import styled from 'styled-components'
|
||||
import background from '../../images/home_hero_bg.png'
|
||||
|
||||
export const Hero = styled.main`
|
||||
export const Hero = styled.main<{ bg?: boolean }>`
|
||||
position: relative;
|
||||
padding: 50px 0;
|
||||
color: white;
|
||||
@@ -13,7 +13,8 @@ export const Hero = styled.main`
|
||||
`
|
||||
|
||||
export const HeroHeading = styled.h1`
|
||||
font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
font-family:
|
||||
'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
text-transform: uppercase;
|
||||
`
|
||||
@@ -1,16 +1,12 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import { PageProps } from 'gatsby'
|
||||
|
||||
import Navibar from './navibar'
|
||||
import HeroSection from './herosection'
|
||||
import Footer from './footer'
|
||||
|
||||
type DataProps = {
|
||||
children?: React.ReactNode
|
||||
heroSection: boolean
|
||||
}
|
||||
import type { LayoutProps } from '../types'
|
||||
|
||||
const Layout: React.FC<PageProps<DataProps>> = ({
|
||||
const Layout: React.FC<LayoutProps> = ({
|
||||
location,
|
||||
children,
|
||||
heroSection = true,
|
||||
|
||||
@@ -1,49 +1,34 @@
|
||||
import React from 'react'
|
||||
import { Link, PageProps } from 'gatsby'
|
||||
import { Link } from 'gatsby'
|
||||
|
||||
import dcLogo from '../../images/dclogo.png'
|
||||
|
||||
import { Container } from '../shared'
|
||||
import { logoStyles, CustomNavBar, ulStyles, Li, StyledLink } from './style'
|
||||
|
||||
import { pathPrefix } from '../../../gatsby-config.js'
|
||||
import { pathPrefix } from '../../../gatsby-config'
|
||||
|
||||
const naviLinks = [
|
||||
{
|
||||
name: 'Home',
|
||||
url: '/',
|
||||
active: 'no'
|
||||
},
|
||||
{
|
||||
name: 'About',
|
||||
url: '/about/',
|
||||
active: 'no'
|
||||
},
|
||||
{
|
||||
name: 'Blog',
|
||||
url: '/blog/',
|
||||
active: 'no'
|
||||
},
|
||||
{
|
||||
name: 'FAQ',
|
||||
url: '/faq/',
|
||||
active: 'no'
|
||||
},
|
||||
import type { NavibarProps } from '../../types'
|
||||
|
||||
interface NaviLink {
|
||||
name: string
|
||||
url: string
|
||||
active: 'yes' | 'no'
|
||||
}
|
||||
|
||||
const naviLinks: NaviLink[] = [
|
||||
{ name: 'Home', url: '/', active: 'no' },
|
||||
{ name: 'About', url: '/about/', active: 'no' },
|
||||
{ name: 'Blog', url: '/blog/', active: 'no' },
|
||||
{ name: 'Feed', url: '/feed/', active: 'no' },
|
||||
{ name: 'FAQ', url: '/faq/', active: 'no' },
|
||||
{
|
||||
name: 'Documentation',
|
||||
url: 'https://docs.datacontroller.io/',
|
||||
active: 'no'
|
||||
},
|
||||
{
|
||||
name: 'Pricing',
|
||||
url: '/pricing/',
|
||||
active: 'no'
|
||||
},
|
||||
{
|
||||
name: 'Book Demo',
|
||||
url: '/contact/',
|
||||
active: 'no'
|
||||
},
|
||||
{ name: 'Pricing', url: '/pricing/', active: 'no' },
|
||||
{ name: 'Book Demo', url: '/contact/', active: 'no' },
|
||||
{
|
||||
name: 'Source Code',
|
||||
url: 'https://git.datacontroller.io/dc/dc',
|
||||
@@ -51,11 +36,7 @@ const naviLinks = [
|
||||
}
|
||||
]
|
||||
|
||||
type DataProps = {
|
||||
location: Location
|
||||
}
|
||||
|
||||
const Navibar: React.FC<PageProps<DataProps>> = ({ location }) => {
|
||||
const Navibar: React.FC<NavibarProps> = ({ location }) => {
|
||||
naviLinks.forEach((link) => (link.active = 'no'))
|
||||
const currentLink = naviLinks.find(
|
||||
(link) => pathPrefix + link.url === location?.pathname
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from 'react'
|
||||
import * as React from 'react'
|
||||
import styled, { css } from 'styled-components'
|
||||
import { Link } from 'gatsby'
|
||||
|
||||
// styles
|
||||
export const logoStyles = {
|
||||
height: '55px'
|
||||
}
|
||||
@@ -295,7 +294,6 @@ const LinkUnderlineStyles = css`
|
||||
transition: opacity 0.3s ease;
|
||||
`
|
||||
|
||||
// styled components
|
||||
export const Li = styled.li`
|
||||
position: relative;
|
||||
@media (min-width: 992px) {
|
||||
@@ -315,7 +313,14 @@ export const Li = styled.li`
|
||||
}
|
||||
}
|
||||
`
|
||||
export const StyledLink = styled((props) => <Link {...props} />)`
|
||||
|
||||
interface StyledLinkProps {
|
||||
active?: string
|
||||
}
|
||||
|
||||
export const StyledLink = styled(({ active, ...rest }: any) => (
|
||||
<Link {...rest} />
|
||||
))<StyledLinkProps>`
|
||||
padding-right: 0.8rem !important;
|
||||
padding-left: 0.8rem !important;
|
||||
color: white !important;
|
||||
+36
-37
@@ -1,9 +1,16 @@
|
||||
import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { useStaticQuery, graphql } from 'gatsby'
|
||||
|
||||
const Seo = ({ description, lang, meta, title, previewImg = undefined }) => {
|
||||
import type { SeoProps } from '../types'
|
||||
|
||||
const Seo: React.FC<SeoProps> = ({
|
||||
description = '',
|
||||
lang = 'en',
|
||||
meta = [],
|
||||
title = '',
|
||||
previewImg
|
||||
}) => {
|
||||
const { site } = useStaticQuery(graphql`
|
||||
query {
|
||||
site {
|
||||
@@ -37,44 +44,36 @@ const Seo = ({ description, lang, meta, title, previewImg = undefined }) => {
|
||||
lang
|
||||
}}
|
||||
title={pageTitle}
|
||||
meta={[
|
||||
{ name: 'author', property: 'author', content: author },
|
||||
link={[
|
||||
{
|
||||
name: 'description',
|
||||
property: 'og:description',
|
||||
content: metaDescription
|
||||
},
|
||||
// { name: 'facebook:site', content: '', },
|
||||
{ name: 'image', property: 'og:image', content: image },
|
||||
{
|
||||
name: `linkedin:site`,
|
||||
content: site.siteMetadata?.social?.linkedin || ``
|
||||
},
|
||||
{ name: `twitter:card`, content: `summary` },
|
||||
// { name: `twitter:creator`, content: site.siteMetadata?.social?.twitter || `` },
|
||||
{ name: `twitter:description`, content: metaDescription },
|
||||
// { name: 'twitter:site', content: `${site?.twitter}`, },
|
||||
{ name: `twitter:title`, content: title },
|
||||
// { name: 'youtube:site', content: `${site?.youtube}`, },
|
||||
{ property: `og:title`, content: title },
|
||||
{ property: `og:type`, content: `website` }
|
||||
].concat(meta)}
|
||||
rel: 'alternate',
|
||||
type: 'application/rss+xml',
|
||||
title: `${defaultTitle} RSS Feed`,
|
||||
href: `${siteUrl}rss.xml`
|
||||
}
|
||||
]}
|
||||
meta={(
|
||||
[
|
||||
{ name: 'author', property: 'author', content: author },
|
||||
{
|
||||
name: 'description',
|
||||
property: 'og:description',
|
||||
content: metaDescription
|
||||
},
|
||||
{ name: 'image', property: 'og:image', content: image },
|
||||
{
|
||||
name: `linkedin:site`,
|
||||
content: site.siteMetadata?.social?.linkedin || ``
|
||||
},
|
||||
{ name: `twitter:card`, content: `summary` },
|
||||
{ name: `twitter:description`, content: metaDescription },
|
||||
{ name: `twitter:title`, content: title },
|
||||
{ property: `og:title`, content: title },
|
||||
{ property: `og:type`, content: `website` }
|
||||
] as Array<{ name?: string; property?: string; content?: string }>
|
||||
).concat(meta)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
Seo.defaultProps = {
|
||||
lang: `en`,
|
||||
meta: [],
|
||||
description: ``,
|
||||
title: ``
|
||||
}
|
||||
|
||||
Seo.propTypes = {
|
||||
description: PropTypes.string,
|
||||
lang: PropTypes.string,
|
||||
meta: PropTypes.arrayOf(PropTypes.object),
|
||||
title: PropTypes.string
|
||||
}
|
||||
|
||||
export default Seo
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import React from 'react'
|
||||
import * as React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { PageProps } from 'gatsby'
|
||||
|
||||
type DataProps = {
|
||||
children?: React.ReactNode
|
||||
}
|
||||
import type { ContainerProps } from '../../types'
|
||||
|
||||
const StyledDiv = styled.div`
|
||||
@media (min-width: 576px) {
|
||||
@@ -13,6 +10,6 @@ const StyledDiv = styled.div`
|
||||
}
|
||||
`
|
||||
|
||||
export const Container: React.FC<PageProps<DataProps>> = ({ children }) => {
|
||||
export const Container: React.FC<ContainerProps> = ({ children }) => {
|
||||
return <StyledDiv className="container">{children}</StyledDiv>
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import styled from 'styled-components'
|
||||
|
||||
import { FaEnvelope } from 'react-icons/fa'
|
||||
|
||||
export const StyledLink = styled((props) => <Link {...props} />)`
|
||||
export const StyledLink = styled(Link)`
|
||||
color: rgb(255, 255, 255);
|
||||
background-color: rgb(144, 196, 69);
|
||||
border-radius: 0px;
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
import React from 'react'
|
||||
import * as React from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import { PageProps } from 'gatsby'
|
||||
import { BottomSectionArrow } from './styledComponents'
|
||||
import { Container } from './'
|
||||
|
||||
type DataProps = {
|
||||
children?: React.ReactNode
|
||||
color?: string
|
||||
bgColor?: string
|
||||
bottomArrow?: boolean
|
||||
}
|
||||
import type { SectionProps } from '../../types'
|
||||
|
||||
const StyledSection = styled.div`
|
||||
const StyledSection = styled.div<{ color?: string; bgColor?: string }>`
|
||||
position: relative;
|
||||
padding: 50px 0;
|
||||
color: ${(props) => props.color || 'white'};
|
||||
background-color: ${(props) => props.bgColor || '#314351'};
|
||||
`
|
||||
|
||||
export const Section: React.FC<PageProps<DataProps>> = ({
|
||||
export const Section: React.FC<SectionProps> = ({
|
||||
children,
|
||||
bgColor,
|
||||
color,
|
||||
|
||||
+10
-10
@@ -1,6 +1,8 @@
|
||||
import React from 'react'
|
||||
import * as React from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import type { SolidButtonProps, OutlineButtonProps } from '../../types'
|
||||
|
||||
const BottomArrow = styled.div`
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
@@ -9,9 +11,6 @@ const BottomArrow = styled.div`
|
||||
background: inherit;
|
||||
transform: translateX(-50%) rotate(45deg);
|
||||
left: 50%;
|
||||
// right: 0;
|
||||
// margin-left: auto;
|
||||
// margin-right: auto;
|
||||
z-index: 10;
|
||||
`
|
||||
const BottomArrowWrapper = styled.div`
|
||||
@@ -29,16 +28,17 @@ export const BottomSectionArrow = () => (
|
||||
</BottomArrowWrapper>
|
||||
)
|
||||
|
||||
export const SectionHeading = styled.h2`
|
||||
export const SectionHeading = styled.h2<{ center?: string }>`
|
||||
text-align: ${(props) => (props.center === 'no' ? 'left' : 'center')};
|
||||
letter-spacing: 1px;
|
||||
font-weight: 400;
|
||||
font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
font-family:
|
||||
'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
text-transform: uppercase;
|
||||
`
|
||||
|
||||
export const SectionDesc = styled.p`
|
||||
export const SectionDesc = styled.p<{ center?: string; opacity?: string }>`
|
||||
text-align: ${(props) => (props.center === 'no' ? 'left' : 'center')};
|
||||
opacity: ${(props) => props.opacity ?? 0.6};
|
||||
a {
|
||||
@@ -59,12 +59,12 @@ const StyledSolidButton = styled.button`
|
||||
}
|
||||
`
|
||||
|
||||
export const SolidButton = ({
|
||||
export const SolidButton: React.FC<SolidButtonProps> = ({
|
||||
children,
|
||||
theme = 'light',
|
||||
type = 'submit',
|
||||
disabled = false,
|
||||
onClick = undefined
|
||||
onClick
|
||||
}) => (
|
||||
<StyledSolidButton
|
||||
type={type}
|
||||
@@ -83,7 +83,7 @@ const StyledOutlineButton = styled.button`
|
||||
border-width: 2px;
|
||||
`
|
||||
|
||||
export const OutlineButton = ({ children }) => (
|
||||
export const OutlineButton: React.FC<OutlineButtonProps> = ({ children }) => (
|
||||
<StyledOutlineButton type="button" className="btn btn-outline-light">
|
||||
{children}
|
||||
</StyledOutlineButton>
|
||||
@@ -34,7 +34,7 @@ export class NostrController extends EventEmitter {
|
||||
/**
|
||||
* Function will publish provided event to the provided relays
|
||||
*/
|
||||
publishEvent = async (event: Event, relays: string[]) => {
|
||||
publishEvent = async (event: Event, relays: string[]): Promise<string[]> => {
|
||||
const simplePool = new SimplePool()
|
||||
const promises = simplePool.publish(relays, event)
|
||||
|
||||
@@ -47,7 +47,7 @@ export class NostrController extends EventEmitter {
|
||||
})
|
||||
|
||||
if (publishedRelays.length === 0) {
|
||||
const failedPublishes: any[] = []
|
||||
const failedPublishes: Array<{ relay: string; error: string }> = []
|
||||
const fallbackRejectionReason =
|
||||
'Attempt to publish an event has been rejected with unknown reason.'
|
||||
|
||||
@@ -92,7 +92,7 @@ export class NostrController extends EventEmitter {
|
||||
return Promise.resolve(signedEvent)
|
||||
}
|
||||
|
||||
nip04Encrypt = async (receiver: string, content: string) => {
|
||||
nip04Encrypt = async (receiver: string, content: string): Promise<string> => {
|
||||
if (!this.generatedKeys) {
|
||||
throw new Error(`Private & public key pair is not found.`)
|
||||
}
|
||||
@@ -113,7 +113,7 @@ export class NostrController extends EventEmitter {
|
||||
* @param isSigner Boolean indicating whether the recipient is a signer or viewer.
|
||||
* @param setAuthUrl Function to set the authentication URL in the component state.
|
||||
*/
|
||||
sendDM = async (pubkey: string, message: string) => {
|
||||
sendDM = async (pubkey: string, message: string): Promise<string[]> => {
|
||||
// Set up timeout promise to handle encryption timeout
|
||||
const timeoutPromise = new Promise<never>((_, reject) => {
|
||||
setTimeout(() => {
|
||||
@@ -159,7 +159,7 @@ export class NostrController extends EventEmitter {
|
||||
* @param hexKey hex private or public key
|
||||
* @returns whether or not is key valid
|
||||
*/
|
||||
validateHex = (hexKey: string) => {
|
||||
validateHex = (hexKey: string): RegExpMatchArray | null => {
|
||||
return hexKey.match(/^[a-f0-9]{64}$/)
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ export class NostrController extends EventEmitter {
|
||||
return { private: nip19.nsecEncode(nsec), public: getPublicKey(nsec) }
|
||||
}
|
||||
|
||||
verifySignedEvent = (event: SignedEvent) => {
|
||||
verifySignedEvent = (event: SignedEvent): void => {
|
||||
const isGood = verifyEvent(event)
|
||||
|
||||
if (!isGood) {
|
||||
|
||||
+8
-3
@@ -1,5 +1,4 @@
|
||||
import React from 'react'
|
||||
// import { Link } from 'gatsby'
|
||||
import * as React from 'react'
|
||||
|
||||
import Layout from '../components/layout'
|
||||
import Seo from '../components/seo'
|
||||
@@ -7,7 +6,13 @@ import Seo from '../components/seo'
|
||||
import { Section } from '../components/shared'
|
||||
import { SideBar } from '../templates/sidebar'
|
||||
|
||||
const NotFound = ({ data, location, pageContext }) => (
|
||||
import type { PageProps } from 'gatsby'
|
||||
import type { PageContext } from '../types'
|
||||
|
||||
const NotFound: React.FC<PageProps<unknown, PageContext>> = ({
|
||||
location,
|
||||
pageContext
|
||||
}) => (
|
||||
<Layout location={location} heroSection={false}>
|
||||
<Seo title="Page Not Found" />
|
||||
<Section color="black" bgColor="white" bottomArrow={false}>
|
||||
|
||||
+3
-13
@@ -1,5 +1,5 @@
|
||||
import { PageProps, Link, graphql } from 'gatsby'
|
||||
import React from 'react'
|
||||
import { PageProps, Link } from 'gatsby'
|
||||
import * as React from 'react'
|
||||
|
||||
import Layout from '../components/layout'
|
||||
import Seo from '../components/seo'
|
||||
@@ -10,17 +10,7 @@ import {
|
||||
SectionDesc
|
||||
} from '../components/shared/styledComponents'
|
||||
|
||||
type DataProps = {
|
||||
site: {
|
||||
meta: {
|
||||
title: string
|
||||
description: string
|
||||
social: { linkedin: string }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const About: React.FC<PageProps<DataProps>> = ({ data, location }) => {
|
||||
const About: React.FC<PageProps<unknown>> = ({ location }) => {
|
||||
return (
|
||||
<Layout
|
||||
location={location}
|
||||
|
||||
+11
-21
@@ -1,5 +1,5 @@
|
||||
import { PageProps, Link, graphql } from 'gatsby'
|
||||
import React, { useState } from 'react'
|
||||
import { PageProps } from 'gatsby'
|
||||
import * as React from 'react'
|
||||
|
||||
import Layout from '../components/layout'
|
||||
import Seo from '../components/seo'
|
||||
@@ -22,25 +22,15 @@ import contactBg from '../images/contact_bg.jpg'
|
||||
import '../styledComponents/contact.css'
|
||||
import { NostrController } from '../controllers'
|
||||
|
||||
type DataProps = {
|
||||
site: {
|
||||
meta: {
|
||||
title: string
|
||||
description: string
|
||||
social: { linkedin: string }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Contact: React.FC<PageProps<DataProps>> = ({ data, location }) => {
|
||||
const Contact: React.FC<PageProps<unknown>> = ({ location }) => {
|
||||
const nostrController = NostrController.getInstance()
|
||||
|
||||
const [name, setName] = useState<string>()
|
||||
const [email, setEmail] = useState<string>()
|
||||
const [emailError, setEmailError] = useState<boolean>(false)
|
||||
const [subject, setSubject] = useState<string>()
|
||||
const [message, setMessage] = useState<string>()
|
||||
const [notification, setNotification] = useState<string>()
|
||||
const [name, setName] = React.useState<string>()
|
||||
const [email, setEmail] = React.useState<string>()
|
||||
const [emailError, setEmailError] = React.useState<boolean>(false)
|
||||
const [subject, setSubject] = React.useState<string>()
|
||||
const [message, setMessage] = React.useState<string>()
|
||||
const [notification, setNotification] = React.useState<string>()
|
||||
|
||||
const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value
|
||||
@@ -98,7 +88,7 @@ Email: ${email}
|
||||
Subject: ${subject}
|
||||
Message: ${message}`
|
||||
)
|
||||
.catch((err) => {
|
||||
.catch((err: unknown) => {
|
||||
setNotification(
|
||||
`Something went wrong. Please check the console for more information. Please try one more time.`
|
||||
)
|
||||
@@ -176,7 +166,7 @@ Message: ${message}`
|
||||
className="form-control contactFormStyles"
|
||||
id="mesage"
|
||||
name="message"
|
||||
rows="5"
|
||||
rows={5}
|
||||
rules="required|max:200"
|
||||
onChange={(evt) => {
|
||||
setMessage(evt.target.value)
|
||||
|
||||
+3
-13
@@ -1,5 +1,5 @@
|
||||
import { PageProps, Link, graphql } from 'gatsby'
|
||||
import React from 'react'
|
||||
import { PageProps, Link } from 'gatsby'
|
||||
import * as React from 'react'
|
||||
|
||||
import Layout from '../components/layout'
|
||||
import Seo from '../components/seo'
|
||||
@@ -13,17 +13,7 @@ import {
|
||||
|
||||
import { FAQ } from '../styledComponents/faq'
|
||||
|
||||
type DataProps = {
|
||||
site: {
|
||||
meta: {
|
||||
title: string
|
||||
description: string
|
||||
social: { linkedin: string }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Faq: React.FC<PageProps<DataProps>> = ({ data, location }) => {
|
||||
const Faq: React.FC<PageProps<unknown>> = ({ location }) => {
|
||||
return (
|
||||
<Layout
|
||||
location={location}
|
||||
|
||||
+17
-25
@@ -1,5 +1,5 @@
|
||||
import { Link, graphql } from 'gatsby'
|
||||
import React from 'react'
|
||||
import * as React from 'react'
|
||||
import { Link, graphql, PageProps } from 'gatsby'
|
||||
import Gallery from '@browniebroke/gatsby-image-gallery'
|
||||
import { IGatsbyImageData } from 'gatsby-plugin-image'
|
||||
|
||||
@@ -34,31 +34,31 @@ import dcDesign from '../images/data-controller-design.png'
|
||||
|
||||
interface ImageSharpEdge {
|
||||
node: {
|
||||
name: string
|
||||
childImageSharp: {
|
||||
thumb: IGatsbyImageData
|
||||
full: IGatsbyImageData
|
||||
meta: {
|
||||
originalName: string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface PageProps {
|
||||
data: {
|
||||
images: {
|
||||
edges: ImageSharpEdge[]
|
||||
}
|
||||
interface IndexPageData {
|
||||
images: {
|
||||
edges: ImageSharpEdge[]
|
||||
}
|
||||
}
|
||||
|
||||
const Home: React.FC<PageProps> = ({ data, location }) => {
|
||||
const CustomWrapper: React.FC<{
|
||||
children?: React.ReactNode
|
||||
onClick?: () => void
|
||||
}> = ({ children, onClick }) => (
|
||||
<ThumbnailWrapper onClick={onClick}>{children}</ThumbnailWrapper>
|
||||
)
|
||||
|
||||
const Home: React.FC<PageProps<IndexPageData>> = ({ data, location }) => {
|
||||
const images = data.images.edges.map(({ node }) => ({
|
||||
...node.childImageSharp,
|
||||
// Use original name as caption.
|
||||
// The `originalName` is queried in a nested field,
|
||||
// but the `Gallery` component expects `caption` at the top level.
|
||||
caption: node.childImageSharp.meta.originalName
|
||||
caption: node.name
|
||||
}))
|
||||
|
||||
return (
|
||||
@@ -231,13 +231,7 @@ const Home: React.FC<PageProps> = ({ data, location }) => {
|
||||
</Section>
|
||||
<Section color="black" bgColor="white">
|
||||
<SectionHeading>See How it Looks</SectionHeading>
|
||||
<Gallery
|
||||
images={images}
|
||||
gutter={0}
|
||||
customWrapper={({ children, onClick }) => (
|
||||
<ThumbnailWrapper onClick={onClick}>{children}</ThumbnailWrapper>
|
||||
)}
|
||||
/>
|
||||
<Gallery images={images} gutter="0" customWrapper={CustomWrapper} />
|
||||
</Section>
|
||||
<ScheduleDemo />
|
||||
</Layout>
|
||||
@@ -254,6 +248,7 @@ export const pageQuery = graphql`
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
name
|
||||
childImageSharp {
|
||||
thumb: gatsbyImageData(
|
||||
width: 300
|
||||
@@ -261,9 +256,6 @@ export const pageQuery = graphql`
|
||||
placeholder: BLURRED
|
||||
)
|
||||
full: gatsbyImageData(layout: FULL_WIDTH)
|
||||
meta: fixed {
|
||||
originalName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import { graphql } from 'gatsby'
|
||||
import * as React from 'react'
|
||||
import { graphql, PageProps } from 'gatsby'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import Layout from '../components/layout'
|
||||
@@ -7,6 +7,8 @@ import Seo from '../components/seo'
|
||||
|
||||
import { Section } from '../components/shared'
|
||||
|
||||
import type { PostNode, PageContext } from '../types'
|
||||
|
||||
const StyledMarkdown = styled.div`
|
||||
color: rgb(102, 102, 102);
|
||||
h2,
|
||||
@@ -18,13 +20,20 @@ const StyledMarkdown = styled.div`
|
||||
h2 {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica,
|
||||
Arial, sans-serif;
|
||||
font-family:
|
||||
'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
}
|
||||
`
|
||||
|
||||
export default function Template({ data, location }) {
|
||||
const { frontmatter, html } = data?.markdownRemark
|
||||
interface MarkdownPageData {
|
||||
markdownRemark: PostNode
|
||||
}
|
||||
|
||||
const MarkdownPageTemplate: React.FC<
|
||||
PageProps<MarkdownPageData, PageContext>
|
||||
> = ({ data, location }) => {
|
||||
const { frontmatter, html } = data.markdownRemark
|
||||
return (
|
||||
<Layout
|
||||
location={location}
|
||||
@@ -39,6 +48,8 @@ export default function Template({ data, location }) {
|
||||
)
|
||||
}
|
||||
|
||||
export default MarkdownPageTemplate
|
||||
|
||||
export const pageQuery = graphql`
|
||||
query ($id: String!) {
|
||||
markdownRemark(id: { eq: $id }) {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import styled from 'styled-components'
|
||||
|
||||
export const StyledHeading = styled.h5`
|
||||
font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
font-family:
|
||||
'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
@@ -1,17 +0,0 @@
|
||||
import styled from 'styled-components'
|
||||
|
||||
export const StyledHeading = styled.h3`
|
||||
font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
`
|
||||
|
||||
export const StyledLabel = styled.label`
|
||||
margin-bottom: 0;
|
||||
`
|
||||
|
||||
export const ContactBackground = styled.img.attrs((props) => ({
|
||||
alt: props.info || '',
|
||||
title: props.info || ''
|
||||
}))`
|
||||
max-width: 100%;
|
||||
`
|
||||
@@ -0,0 +1,20 @@
|
||||
import styled from 'styled-components'
|
||||
|
||||
export const StyledHeading = styled.h3`
|
||||
font-family:
|
||||
'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
`
|
||||
|
||||
export const StyledLabel = styled.label`
|
||||
margin-bottom: 0;
|
||||
`
|
||||
|
||||
export const ContactBackground = styled.img.attrs<{ info?: string }>(
|
||||
(props) => ({
|
||||
alt: props.info || '',
|
||||
title: props.info || ''
|
||||
})
|
||||
)`
|
||||
max-width: 100%;
|
||||
`
|
||||
@@ -1,15 +1,18 @@
|
||||
import React from 'react'
|
||||
import * as React from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import { FcFaq } from 'react-icons/fc'
|
||||
|
||||
import type { FaqProps } from '../types'
|
||||
|
||||
const StyledFaq = styled.div`
|
||||
margin-bottom: 20px;
|
||||
`
|
||||
|
||||
const StyledQuestion = styled.h5`
|
||||
color: #222;
|
||||
font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
font-family:
|
||||
'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
text-transform: uppercase;
|
||||
`
|
||||
@@ -33,7 +36,7 @@ const FaqDetails = styled.div`
|
||||
margin-bottom: 20px;
|
||||
`
|
||||
|
||||
export const FAQ = ({ question, answer }) => (
|
||||
export const FAQ: React.FC<FaqProps> = ({ question, answer }) => (
|
||||
<StyledFaq>
|
||||
<IconHolderStyled>
|
||||
<FcFaq size={40} />
|
||||
@@ -1,6 +1,9 @@
|
||||
import React from 'react'
|
||||
import * as React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { ImArrowRight } from 'react-icons/im'
|
||||
import type { IconType } from 'react-icons'
|
||||
|
||||
import type { ArtProps, ReasonProps, FeatureProps } from '../types'
|
||||
|
||||
export const CenteredText = styled.p`
|
||||
text-align: center;
|
||||
@@ -9,20 +12,19 @@ export const CenteredText = styled.p`
|
||||
margin: 0 auto;
|
||||
font-size: 1.2rem;
|
||||
line-height: 1;
|
||||
font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
font-family:
|
||||
'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
`
|
||||
export const Art = styled.img.attrs((props) => ({
|
||||
export const Art = styled.img.attrs<ArtProps>((props) => ({
|
||||
alt: props.info || '',
|
||||
title: props.info || ''
|
||||
}))`
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
height: auto;
|
||||
`
|
||||
|
||||
const ArrowHolder = styled.div`
|
||||
const ArrowHolder = styled.div<{ bgColor?: string }>`
|
||||
display: inline-block;
|
||||
border-radius: 50px;
|
||||
padding: 15px 17px;
|
||||
@@ -30,7 +32,7 @@ const ArrowHolder = styled.div`
|
||||
background-color: ${(props) =>
|
||||
props.bgColor === 'red' ? '#ed1c1c' : '#90c445'};
|
||||
`
|
||||
const RightArrow = ({ bgColor = '' }) => (
|
||||
const RightArrow: React.FC<{ bgColor?: string }> = ({ bgColor = '' }) => (
|
||||
<ArrowHolder bgColor={bgColor}>
|
||||
<ImArrowRight size={24} />
|
||||
</ArrowHolder>
|
||||
@@ -46,7 +48,7 @@ const ReasonDesc = styled.p`
|
||||
text-transform: uppercase;
|
||||
`
|
||||
|
||||
export const Reason = ({ text, bgColor = '' }) => (
|
||||
export const Reason: React.FC<ReasonProps> = ({ text, bgColor = '' }) => (
|
||||
<>
|
||||
<RightArrow bgColor={bgColor} />
|
||||
<ReasonDesc>{text}</ReasonDesc>
|
||||
@@ -62,7 +64,7 @@ const IconHolderStyled = styled.div`
|
||||
vertical-align: top;
|
||||
`
|
||||
|
||||
const IconHolder = ({ Icon }) => {
|
||||
const IconHolder = ({ Icon }: { Icon: IconType }) => {
|
||||
return (
|
||||
<IconHolderStyled>
|
||||
<Icon size={24} />
|
||||
@@ -77,7 +79,8 @@ const FeatureDetails = styled.div`
|
||||
margin-bottom: 20px;
|
||||
`
|
||||
const FeatureTitle = styled.h5`
|
||||
font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
font-family:
|
||||
'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
text-transform: uppercase;
|
||||
`
|
||||
@@ -86,7 +89,12 @@ const FeatureDesc = styled.p`
|
||||
font-size: 0.9rem;
|
||||
`
|
||||
|
||||
export const Feature = ({ title, desc, Icon, className }) => (
|
||||
export const Feature: React.FC<FeatureProps> = ({
|
||||
title,
|
||||
desc,
|
||||
Icon,
|
||||
className
|
||||
}) => (
|
||||
<div className={className ?? 'col-md-4'}>
|
||||
<IconHolder Icon={Icon} />
|
||||
<FeatureDetails>
|
||||
@@ -0,0 +1,35 @@
|
||||
@font-face {
|
||||
font-family: 'Montserrat';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url('/fonts/Montserrat-300.woff2') format('woff2');
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
|
||||
U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
|
||||
U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Montserrat';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('/fonts/Montserrat-400.woff2') format('woff2');
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
|
||||
U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
|
||||
U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Montserrat';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url('/fonts/Montserrat-500.woff2') format('woff2');
|
||||
unicode-range:
|
||||
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
|
||||
U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
|
||||
U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
+26
-16
@@ -1,5 +1,5 @@
|
||||
import { Link, graphql } from 'gatsby'
|
||||
import React from 'react'
|
||||
import { Link, graphql, PageProps } from 'gatsby'
|
||||
import * as React from 'react'
|
||||
import kebabCase from 'lodash/kebabCase'
|
||||
import styled from 'styled-components'
|
||||
|
||||
@@ -16,27 +16,39 @@ import {
|
||||
import Post from './postpreview'
|
||||
import { SideBar } from './sidebar'
|
||||
|
||||
const BlogListTemplate = ({ data, location, pageContext }) => {
|
||||
import type { PostNode, PageContext } from '../types'
|
||||
|
||||
interface BlogListData {
|
||||
remark: {
|
||||
posts: Array<{ post: PostNode }>
|
||||
}
|
||||
}
|
||||
|
||||
const BlogListTemplate: React.FC<PageProps<BlogListData, PageContext>> = ({
|
||||
data,
|
||||
location,
|
||||
pageContext
|
||||
}) => {
|
||||
const posts = data.remark.posts
|
||||
|
||||
const iniPath =
|
||||
pageContext.page == 'index'
|
||||
const iniPath: string =
|
||||
pageContext.page === 'index'
|
||||
? `/blog/`
|
||||
: pageContext.page == 'year'
|
||||
? `/${pageContext.year}/`
|
||||
: pageContext.page == 'category'
|
||||
? `/category/${kebabCase(pageContext.tag)}/`
|
||||
: null
|
||||
: pageContext.page === 'year'
|
||||
? `/${pageContext.year}/`
|
||||
: pageContext.page === 'category'
|
||||
? `/category/${kebabCase(pageContext.tag ?? '')}/`
|
||||
: `/blog/`
|
||||
|
||||
const pageInfo = `Page ${pageContext.currentPage} of ${pageContext.numPages}`
|
||||
Array.from({ length: 5 }, (v, k) => k + 1)
|
||||
const paginationJSX = Array.from(
|
||||
{ length: pageContext.numPages },
|
||||
{ length: pageContext.numPages ?? 0 },
|
||||
(_, i) => i + 1
|
||||
).map((pageIndex) => {
|
||||
const link = pageIndex === 1 ? iniPath : `${iniPath}page/${pageIndex}`
|
||||
return (
|
||||
<Link
|
||||
key={pageIndex}
|
||||
to={link}
|
||||
className={`btn btn-outline-dark btn-sm ${
|
||||
pageIndex === pageContext.currentPage ? 'disabled' : ''
|
||||
@@ -62,8 +74,8 @@ const BlogListTemplate = ({ data, location, pageContext }) => {
|
||||
<div className="row">
|
||||
<div className="col-md-7">
|
||||
<div className="row">
|
||||
{posts.map((data, i) => (
|
||||
<Post key={i} post={data.post} />
|
||||
{posts.map(({ post }, i) => (
|
||||
<Post key={i} post={post} />
|
||||
))}
|
||||
</div>
|
||||
<span className="float-start">{paginationJSX}</span>
|
||||
@@ -114,5 +126,3 @@ export const pageQuery = graphql`
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// tags
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import { Link, graphql } from 'gatsby'
|
||||
import * as React from 'react'
|
||||
import { graphql, PageProps } from 'gatsby'
|
||||
|
||||
import Layout from '../components/layout'
|
||||
import Seo from '../components/seo'
|
||||
@@ -8,15 +8,19 @@ import { Section } from '../components/shared'
|
||||
import Post from './post'
|
||||
import { SideBar } from './sidebar'
|
||||
|
||||
const BlogPostTemplate = ({ data, location, pageContext }) => {
|
||||
import type { PostNode, PageContext } from '../types'
|
||||
|
||||
const BlogPostTemplate: React.FC<
|
||||
PageProps<{ post: PostNode }, PageContext>
|
||||
> = ({ data, location, pageContext }) => {
|
||||
const { post } = data
|
||||
const { previewImg } = post.frontmatter
|
||||
|
||||
return (
|
||||
<Layout location={location} heroSection={false}>
|
||||
<Seo
|
||||
title={post?.frontmatter?.title}
|
||||
description={post?.frontmatter?.description}
|
||||
title={post.frontmatter.title}
|
||||
description={post.frontmatter.description}
|
||||
previewImg={
|
||||
previewImg?.childImageSharp?.gatsbyImageData?.images?.fallback?.src
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { Link, graphql, useStaticQuery } from 'gatsby'
|
||||
import React from 'react'
|
||||
import kebabCase from 'lodash/kebabCase'
|
||||
import { Link, graphql, useStaticQuery, PageProps } from 'gatsby'
|
||||
import * as React from 'react'
|
||||
import { useFlexSearch } from 'react-use-flexsearch'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import Layout from '../components/layout'
|
||||
import Seo from '../components/seo'
|
||||
@@ -17,7 +15,19 @@ import {
|
||||
import Post from './postpreview'
|
||||
import { SideBar } from './sidebar'
|
||||
|
||||
const BlogSearchTemplate = ({ location, pageContext }) => {
|
||||
import type { PageContext, PostNode, SearchPost } from '../types'
|
||||
|
||||
interface SearchData {
|
||||
localSearchBlog: {
|
||||
index: string
|
||||
store: Record<string, unknown>
|
||||
}
|
||||
}
|
||||
|
||||
const BlogSearchTemplate: React.FC<PageProps<SearchData, PageContext>> = ({
|
||||
location,
|
||||
pageContext
|
||||
}) => {
|
||||
const queryData = useStaticQuery(graphql`
|
||||
query {
|
||||
localSearchBlog {
|
||||
@@ -34,12 +44,12 @@ const BlogSearchTemplate = ({ location, pageContext }) => {
|
||||
query,
|
||||
queryData.localSearchBlog.index,
|
||||
queryData.localSearchBlog.store
|
||||
)
|
||||
const postsJSX = []
|
||||
) as SearchPost[]
|
||||
const postsJSX: React.ReactElement[] = []
|
||||
|
||||
if (query) {
|
||||
posts.forEach((post, i) => {
|
||||
const _post = {
|
||||
const _post: PostNode = {
|
||||
id: post.id,
|
||||
html: post.html,
|
||||
fields: {
|
||||
@@ -48,7 +58,7 @@ const BlogSearchTemplate = ({ location, pageContext }) => {
|
||||
frontmatter: {
|
||||
title: post.title,
|
||||
date: post.date,
|
||||
previewImg: post.previewImg
|
||||
previewImg: post.previewImg as PostNode['frontmatter']['previewImg']
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
import { Link, graphql, PageProps } from 'gatsby'
|
||||
import * as React from 'react'
|
||||
import kebabCase from 'lodash/kebabCase'
|
||||
|
||||
import Layout from '../components/layout'
|
||||
import Seo from '../components/seo'
|
||||
|
||||
import { Section } from '../components/shared'
|
||||
import {
|
||||
SectionHeading,
|
||||
SectionDesc,
|
||||
SolidButton
|
||||
} from '../components/shared/styledComponents'
|
||||
|
||||
import Post from './postpreview'
|
||||
import { SideBar } from './sidebar'
|
||||
|
||||
import type { PostNode, PageContext } from '../types'
|
||||
|
||||
interface FeedListData {
|
||||
remark: {
|
||||
posts: Array<{ post: PostNode }>
|
||||
}
|
||||
}
|
||||
|
||||
const FeedListTemplate: React.FC<PageProps<FeedListData, PageContext>> = ({
|
||||
data,
|
||||
location,
|
||||
pageContext
|
||||
}) => {
|
||||
const posts = data.remark.posts
|
||||
|
||||
const iniPath: string =
|
||||
pageContext.page === 'index'
|
||||
? `/feed/`
|
||||
: pageContext.page === 'year'
|
||||
? `/feed/${pageContext.year}/`
|
||||
: pageContext.page === 'category'
|
||||
? `/feed/category/${kebabCase(pageContext.tag ?? '')}/`
|
||||
: `/feed/`
|
||||
|
||||
const pageInfo = `Page ${pageContext.currentPage} of ${pageContext.numPages}`
|
||||
const paginationJSX = Array.from(
|
||||
{ length: pageContext.numPages ?? 0 },
|
||||
(_, i) => i + 1
|
||||
).map((pageIndex) => {
|
||||
const link = pageIndex === 1 ? iniPath : `${iniPath}page/${pageIndex}`
|
||||
return (
|
||||
<Link
|
||||
key={pageIndex}
|
||||
to={link}
|
||||
className={`btn btn-outline-dark btn-sm ${
|
||||
pageIndex === pageContext.currentPage ? 'disabled' : ''
|
||||
}`}
|
||||
style={{
|
||||
borderRadius: '50%',
|
||||
padding: '.375rem .75rem',
|
||||
margin: '0.25rem'
|
||||
}}
|
||||
>
|
||||
{pageIndex}
|
||||
</Link>
|
||||
)
|
||||
})
|
||||
return (
|
||||
<Layout
|
||||
location={location}
|
||||
heading="Data Controller’s Feed"
|
||||
desc="Announcements and social media style updates from the Data Controller team."
|
||||
>
|
||||
<Seo title="Feed" />
|
||||
<Section color="black" bgColor="white" bottomArrow={false}>
|
||||
<div className="row">
|
||||
<div className="col-md-7">
|
||||
<div className="row">
|
||||
{posts.map(({ post }, i) => (
|
||||
<Post key={i} post={post} />
|
||||
))}
|
||||
</div>
|
||||
<span className="float-start">{paginationJSX}</span>
|
||||
<span className="float-end">{pageInfo}</span>
|
||||
</div>
|
||||
<div className="col-md-5">
|
||||
<SideBar
|
||||
pageContext={pageContext}
|
||||
location={location}
|
||||
basePath="/feed"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
export default FeedListTemplate
|
||||
|
||||
export const pageQuery = graphql`
|
||||
query FeedListQuery(
|
||||
$filter: MarkdownRemarkFilterInput!
|
||||
$skip: Int!
|
||||
$limit: Int!
|
||||
) {
|
||||
remark: allMarkdownRemark(
|
||||
filter: $filter
|
||||
limit: $limit
|
||||
skip: $skip
|
||||
sort: { frontmatter: { date: DESC } }
|
||||
) {
|
||||
posts: edges {
|
||||
post: node {
|
||||
html
|
||||
fields {
|
||||
slug
|
||||
}
|
||||
frontmatter {
|
||||
title
|
||||
date(formatString: "MMMM DD, YYYY")
|
||||
author
|
||||
authorLink
|
||||
previewImg {
|
||||
childImageSharp {
|
||||
gatsbyImageData(layout: CONSTRAINED)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
@@ -0,0 +1,71 @@
|
||||
import * as React from 'react'
|
||||
import { graphql, PageProps } from 'gatsby'
|
||||
|
||||
import Layout from '../components/layout'
|
||||
import Seo from '../components/seo'
|
||||
|
||||
import { Section } from '../components/shared'
|
||||
import Post from './post'
|
||||
import { SideBar } from './sidebar'
|
||||
|
||||
import type { PostNode, PageContext } from '../types'
|
||||
|
||||
const FeedPostTemplate: React.FC<
|
||||
PageProps<{ post: PostNode }, PageContext>
|
||||
> = ({ data, location, pageContext }) => {
|
||||
const { post } = data
|
||||
const { previewImg } = post.frontmatter
|
||||
|
||||
return (
|
||||
<Layout location={location} heroSection={false}>
|
||||
<Seo
|
||||
title={post.frontmatter.title}
|
||||
description={post.frontmatter.description}
|
||||
previewImg={
|
||||
previewImg?.childImageSharp?.gatsbyImageData?.images?.fallback?.src
|
||||
}
|
||||
/>
|
||||
<Section color="black" bgColor="white" bottomArrow={false}>
|
||||
<div className="row">
|
||||
<div className="col-md-7">
|
||||
<Post post={post} location={location} />
|
||||
</div>
|
||||
<div className="col-md-5">
|
||||
<SideBar
|
||||
pageContext={pageContext}
|
||||
location={location}
|
||||
basePath="/feed"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
export default FeedPostTemplate
|
||||
|
||||
export const pageQuery = graphql`
|
||||
query FeedPostByPath($id: String!) {
|
||||
post: markdownRemark(id: { eq: $id }) {
|
||||
id
|
||||
html
|
||||
fields {
|
||||
slug
|
||||
}
|
||||
frontmatter {
|
||||
title
|
||||
description
|
||||
date(formatString: "MMMM DD, YYYY")
|
||||
author
|
||||
authorLink
|
||||
previewImg {
|
||||
childImageSharp {
|
||||
gatsbyImageData(layout: CONSTRAINED)
|
||||
}
|
||||
}
|
||||
tags
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
@@ -1,12 +1,14 @@
|
||||
import React from 'react'
|
||||
import * as React from 'react'
|
||||
import { Link } from 'gatsby'
|
||||
import kebabCase from 'lodash/kebabCase'
|
||||
|
||||
import { GatsbyImage } from 'gatsby-plugin-image'
|
||||
import { GatsbyImage, IGatsbyImageData } from 'gatsby-plugin-image'
|
||||
|
||||
import styled from 'styled-components'
|
||||
|
||||
export const StyledLink = styled((props) => <Link {...props} />)`
|
||||
import type { PostNode } from '../../types'
|
||||
|
||||
export const StyledLink = styled(Link)`
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
&:hover {
|
||||
@@ -16,7 +18,8 @@ export const StyledLink = styled((props) => <Link {...props} />)`
|
||||
`
|
||||
|
||||
const StyledTitle = styled.h5`
|
||||
font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
font-family:
|
||||
'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
margin-bottom: 0;
|
||||
`
|
||||
@@ -45,7 +48,8 @@ const StyledContent = styled.div`
|
||||
padding: 15px 0;
|
||||
color: #666666;
|
||||
|
||||
font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
font-family:
|
||||
'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
a {
|
||||
color: black;
|
||||
@@ -116,7 +120,12 @@ const StyledContent = styled.div`
|
||||
}
|
||||
`
|
||||
|
||||
const Post = ({ post, location }) => {
|
||||
interface PostProps {
|
||||
post: PostNode
|
||||
location?: Location
|
||||
}
|
||||
|
||||
const Post: React.FC<PostProps> = ({ post }) => {
|
||||
const tagsJSX = (post.frontmatter?.tags || []).map((tag, index) => (
|
||||
<span key={index}>
|
||||
{index > 0 && ', '}
|
||||
@@ -134,14 +143,18 @@ const Post = ({ post, location }) => {
|
||||
) : (
|
||||
<span>{post.frontmatter.author}</span>
|
||||
)
|
||||
const previewImgData = post.frontmatter.previewImg?.childImageSharp
|
||||
?.gatsbyImageData as IGatsbyImageData | undefined
|
||||
return (
|
||||
<div>
|
||||
<GatsbyImage
|
||||
image={post.frontmatter.previewImg.childImageSharp.gatsbyImageData}
|
||||
style={{ width: '100%', marginBottom: '1rem' }}
|
||||
imgStyle={{ objectFit: 'contain' }}
|
||||
alt={post.frontmatter.title}
|
||||
/>
|
||||
{previewImgData && (
|
||||
<GatsbyImage
|
||||
image={previewImgData}
|
||||
style={{ width: '100%', marginBottom: '1rem' }}
|
||||
imgStyle={{ objectFit: 'contain' }}
|
||||
alt={post.frontmatter.title}
|
||||
/>
|
||||
)}
|
||||
|
||||
<StyledTitle>{post.frontmatter.title}</StyledTitle>
|
||||
<StyledDate>
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import React from 'react'
|
||||
import * as React from 'react'
|
||||
import { Link } from 'gatsby'
|
||||
|
||||
import { GatsbyImage } from 'gatsby-plugin-image'
|
||||
import { GatsbyImage, IGatsbyImageData } from 'gatsby-plugin-image'
|
||||
|
||||
import styled from 'styled-components'
|
||||
|
||||
import getDescription from '../shared/getDescription'
|
||||
|
||||
export const StyledLink = styled((props) => <Link {...props} />)`
|
||||
import type { PostNode } from '../../types'
|
||||
|
||||
export const StyledLink = styled(Link)`
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
&:hover {
|
||||
@@ -17,7 +19,8 @@ export const StyledLink = styled((props) => <Link {...props} />)`
|
||||
`
|
||||
|
||||
const StyledTitle = styled.h5`
|
||||
font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
font-family:
|
||||
'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0;
|
||||
@@ -34,21 +37,31 @@ const StyledDesc = styled.p`
|
||||
margin-bottom: 100px;
|
||||
`
|
||||
|
||||
const Post = ({ post }) => (
|
||||
<div className="col-md-6">
|
||||
<StyledLink to={post.fields.slug}>
|
||||
<GatsbyImage
|
||||
image={post.frontmatter.previewImg.childImageSharp.gatsbyImageData}
|
||||
style={{ minHeight: '150px', maxHeight: '200px' }}
|
||||
imgStyle={{ objectFit: 'contain' }}
|
||||
alt={post.frontmatter.title}
|
||||
/>
|
||||
interface PostPreviewProps {
|
||||
post: PostNode
|
||||
}
|
||||
|
||||
<StyledTitle>{post.frontmatter.title}</StyledTitle>
|
||||
<StyledDate>{post.frontmatter.date}</StyledDate>
|
||||
<StyledDesc>{getDescription(post.html)}</StyledDesc>
|
||||
</StyledLink>
|
||||
</div>
|
||||
)
|
||||
const PostPreview: React.FC<PostPreviewProps> = ({ post }) => {
|
||||
const previewImgData = post.frontmatter.previewImg?.childImageSharp
|
||||
?.gatsbyImageData as IGatsbyImageData | undefined
|
||||
return (
|
||||
<div className="col-md-6">
|
||||
<StyledLink to={post.fields.slug}>
|
||||
{previewImgData && (
|
||||
<GatsbyImage
|
||||
image={previewImgData}
|
||||
style={{ minHeight: '150px', maxHeight: '200px' }}
|
||||
imgStyle={{ objectFit: 'contain' }}
|
||||
alt={post.frontmatter.title}
|
||||
/>
|
||||
)}
|
||||
|
||||
export default Post
|
||||
<StyledTitle>{post.frontmatter.title}</StyledTitle>
|
||||
<StyledDate>{post.frontmatter.date}</StyledDate>
|
||||
<StyledDesc>{getDescription(post.html)}</StyledDesc>
|
||||
</StyledLink>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PostPreview
|
||||
|
||||
@@ -7,7 +7,7 @@ const extractContent = (s: string): string => {
|
||||
return s
|
||||
}
|
||||
const getDescription = (content: string): string => {
|
||||
return extractContent(content).substr(0, 100) + '...'
|
||||
return extractContent(content).slice(0, 100) + '...'
|
||||
}
|
||||
|
||||
export default getDescription
|
||||
|
||||
+66
-20
@@ -1,12 +1,17 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import * as React from 'react'
|
||||
import { Link, navigate } from 'gatsby'
|
||||
import kebabCase from 'lodash/kebabCase'
|
||||
import styled, { css } from 'styled-components'
|
||||
import { pathPrefix } from '../../gatsby-config.js'
|
||||
import { FaRss } from 'react-icons/fa'
|
||||
import { pathPrefix, siteMetadata } from '../../gatsby-config'
|
||||
|
||||
import { StyledHeading } from '../styledComponents/blog'
|
||||
import { SolidButton } from '../components/shared/styledComponents'
|
||||
|
||||
import type { PageContext, RecentPost, TagCount } from '../types'
|
||||
|
||||
const rssFeedUrl = `${siteMetadata.siteUrl}rss.xml`
|
||||
|
||||
const styles = css`
|
||||
color: #314351;
|
||||
opacity: 0.8;
|
||||
@@ -29,10 +34,16 @@ const linkStyles = css`
|
||||
text-decoration: underline;
|
||||
}
|
||||
`
|
||||
const ArchiveLink = styled((props) => <Link {...props} />)`
|
||||
const ArchiveLink = styled(Link)`
|
||||
${linkStyles}
|
||||
`
|
||||
const RecentPostLink = styled((props) => <Link {...props} />)`
|
||||
const RssAnchor = styled.a`
|
||||
${linkStyles}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
`
|
||||
const RecentPostLink = styled(Link)`
|
||||
${linkStyles}
|
||||
padding: 3px;
|
||||
border-bottom: 3px solid #e1e1e1;
|
||||
@@ -59,30 +70,44 @@ const StyledPara = styled.p`
|
||||
${styles}
|
||||
`
|
||||
|
||||
const Archives = ({ archives }) => (
|
||||
interface ArchivesProps {
|
||||
archives: Record<string, number>
|
||||
basePath?: string
|
||||
}
|
||||
|
||||
const Archives: React.FC<ArchivesProps> = ({ archives, basePath = '' }) => (
|
||||
<>
|
||||
{Object.keys(archives)
|
||||
.sort()
|
||||
.reverse()
|
||||
.map((year, index) => (
|
||||
<ArchiveLink key={year} to={`/${year}/`}>
|
||||
.map((year) => (
|
||||
<ArchiveLink key={year} to={`${basePath}/${year}/`}>
|
||||
{year} ({archives[year]})
|
||||
</ArchiveLink>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
|
||||
const Categories = ({ tags }) => (
|
||||
interface CategoriesProps {
|
||||
tags: TagCount[]
|
||||
basePath?: string
|
||||
}
|
||||
|
||||
const Categories: React.FC<CategoriesProps> = ({ tags, basePath = '' }) => (
|
||||
<>
|
||||
{tags.map((tag, i) => (
|
||||
<ArchiveLink key={i} to={`/category/${kebabCase(tag.name)}/`}>
|
||||
<ArchiveLink key={i} to={`${basePath}/category/${kebabCase(tag.name)}/`}>
|
||||
{tag.name} ({tag.totalCount})
|
||||
</ArchiveLink>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
|
||||
const RecentPosts = ({ posts }) => (
|
||||
interface RecentPostsProps {
|
||||
posts: RecentPost[]
|
||||
}
|
||||
|
||||
const RecentPosts: React.FC<RecentPostsProps> = ({ posts }) => (
|
||||
<>
|
||||
{posts.map((post) => (
|
||||
<RecentPostLink key={post.slug} to={post.slug}>
|
||||
@@ -92,22 +117,34 @@ const RecentPosts = ({ posts }) => (
|
||||
</>
|
||||
)
|
||||
|
||||
export const SideBar = ({ pageContext, location, notFoundPage = false }) => {
|
||||
interface SideBarProps {
|
||||
pageContext: PageContext
|
||||
location: Location
|
||||
notFoundPage?: boolean
|
||||
basePath?: string
|
||||
}
|
||||
|
||||
export const SideBar: React.FC<SideBarProps> = ({
|
||||
pageContext,
|
||||
location,
|
||||
notFoundPage = false,
|
||||
basePath = ''
|
||||
}) => {
|
||||
const params = new URLSearchParams(location.search.substring(1))
|
||||
const queryUrl = params?.get('s') || ''
|
||||
const [query, setQuery] = useState(queryUrl)
|
||||
useEffect(() => {
|
||||
const [query, setQuery] = React.useState(queryUrl)
|
||||
React.useEffect(() => {
|
||||
setQuery(queryUrl)
|
||||
}, [queryUrl])
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault()
|
||||
navigate('/search?s=' + query)
|
||||
}
|
||||
|
||||
const _handleKeyDown = (event) => {
|
||||
const _handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (event.key === 'Enter') {
|
||||
handleSubmit(event)
|
||||
navigate('/search?s=' + query)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,13 +167,16 @@ export const SideBar = ({ pageContext, location, notFoundPage = false }) => {
|
||||
type="text"
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
onKeyDown={(e) => _handleKeyDown(e)}
|
||||
onKeyDown={_handleKeyDown}
|
||||
className="form-control"
|
||||
placeholder="Search"
|
||||
aria-label="Search"
|
||||
aria-describedby="button-addon2"
|
||||
/>
|
||||
<SolidButton theme="dark" onClick={handleSubmit}>
|
||||
<SolidButton
|
||||
theme="dark"
|
||||
onClick={() => navigate('/search?s=' + query)}
|
||||
>
|
||||
Search
|
||||
</SolidButton>
|
||||
<form onSubmit={handleSubmit}></form>
|
||||
@@ -168,11 +208,17 @@ export const SideBar = ({ pageContext, location, notFoundPage = false }) => {
|
||||
<>
|
||||
<SideBarSection>
|
||||
<StyledHeading>Archives</StyledHeading>
|
||||
<Archives archives={pageContext.archives} />
|
||||
<Archives archives={pageContext.archives} basePath={basePath} />
|
||||
</SideBarSection>
|
||||
<SideBarSection>
|
||||
<StyledHeading>Categories</StyledHeading>
|
||||
<Categories tags={pageContext.tags} />
|
||||
<Categories tags={pageContext.tags} basePath={basePath} />
|
||||
</SideBarSection>
|
||||
<SideBarSection>
|
||||
<StyledHeading>Subscribe</StyledHeading>
|
||||
<RssAnchor href={rssFeedUrl} target="_blank" rel="noopener">
|
||||
<FaRss /> RSS Feed
|
||||
</RssAnchor>
|
||||
</SideBarSection>
|
||||
</>
|
||||
)}
|
||||
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
declare module '*.png'
|
||||
declare module '*.jpg'
|
||||
declare module '*.jpeg'
|
||||
declare module '*.svg'
|
||||
declare module '*.gif'
|
||||
declare module '*.webp'
|
||||
declare module '*.ico'
|
||||
declare module '*.css'
|
||||
declare module '*.scss'
|
||||
|
||||
declare module 'react-use-flexsearch' {
|
||||
export function useFlexSearch(
|
||||
query: string | null,
|
||||
index: string,
|
||||
store: Record<string, unknown>
|
||||
): Array<Record<string, unknown>>
|
||||
}
|
||||
|
||||
declare module '@browniebroke/gatsby-image-gallery' {
|
||||
import * as React from 'react'
|
||||
const Gallery: React.FC<
|
||||
Record<string, unknown> & { images: Array<Record<string, unknown>> }
|
||||
>
|
||||
export default Gallery
|
||||
}
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
import 'react'
|
||||
|
||||
declare module 'react' {
|
||||
interface InputHTMLAttributes<T> extends HTMLAttributes<T> {
|
||||
rules?: string
|
||||
}
|
||||
interface TextareaHTMLAttributes<T> extends HTMLAttributes<T> {
|
||||
rules?: string
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
export * from './nostr'
|
||||
export * from './site'
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
import { IGatsbyImageData } from 'gatsby-plugin-image'
|
||||
import * as React from 'react'
|
||||
import type { IconType } from 'react-icons'
|
||||
|
||||
export type PageType = 'index' | 'year' | 'category' | 'search'
|
||||
|
||||
export interface RecentPost {
|
||||
slug: string
|
||||
title: string
|
||||
}
|
||||
|
||||
export interface TagCount {
|
||||
name: string
|
||||
totalCount: number
|
||||
}
|
||||
|
||||
export interface PageContext {
|
||||
page: PageType
|
||||
archives: Record<string, number>
|
||||
recentPosts: RecentPost[]
|
||||
tags: TagCount[]
|
||||
filter?: unknown
|
||||
limit?: number
|
||||
skip?: number
|
||||
numPages?: number
|
||||
currentPage?: number
|
||||
year?: string
|
||||
tag?: string
|
||||
}
|
||||
|
||||
export interface PostFrontmatter {
|
||||
title: string
|
||||
date: string
|
||||
author?: string
|
||||
authorLink?: string
|
||||
description?: string
|
||||
tags?: string[]
|
||||
heading?: string
|
||||
desc?: string
|
||||
previewImg?: {
|
||||
childImageSharp?: {
|
||||
gatsbyImageData: IGatsbyImageData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface PostNode {
|
||||
id?: string
|
||||
html: string
|
||||
fields: {
|
||||
slug: string
|
||||
}
|
||||
frontmatter: PostFrontmatter
|
||||
}
|
||||
|
||||
export interface LayoutProps {
|
||||
location: Location
|
||||
heroSection?: boolean
|
||||
heading?: string
|
||||
desc?: string
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
export interface HeroSectionProps {
|
||||
location: Location
|
||||
heading?: string
|
||||
desc?: string
|
||||
}
|
||||
|
||||
export interface NavibarProps {
|
||||
location: Location
|
||||
}
|
||||
|
||||
export interface ContainerProps {
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
export interface SectionProps {
|
||||
children?: React.ReactNode
|
||||
color?: string
|
||||
bgColor?: string
|
||||
bottomArrow?: boolean
|
||||
}
|
||||
|
||||
export interface SeoProps {
|
||||
description?: string
|
||||
lang?: string
|
||||
meta?: Array<{ name?: string; property?: string; content?: string }>
|
||||
title?: string
|
||||
previewImg?: string
|
||||
}
|
||||
|
||||
export interface SolidButtonProps {
|
||||
children: React.ReactNode
|
||||
theme?: 'light' | 'dark'
|
||||
type?: 'button' | 'submit' | 'reset'
|
||||
disabled?: boolean
|
||||
onClick?: React.MouseEventHandler<HTMLButtonElement>
|
||||
}
|
||||
|
||||
export interface OutlineButtonProps {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export interface FaqProps {
|
||||
question: string
|
||||
answer: string
|
||||
}
|
||||
|
||||
export interface FeatureProps {
|
||||
title: string
|
||||
desc: string
|
||||
Icon: IconType
|
||||
className?: string
|
||||
}
|
||||
|
||||
export interface ReasonProps {
|
||||
text: string
|
||||
bgColor?: string
|
||||
}
|
||||
|
||||
export interface ArtProps {
|
||||
src: string
|
||||
info?: string
|
||||
}
|
||||
|
||||
export interface StyledAnchorProps {
|
||||
href: string
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
export type SearchPost = {
|
||||
id: string
|
||||
html: string
|
||||
slug: string
|
||||
title: string
|
||||
date: string
|
||||
previewImg?: PostFrontmatter['previewImg']
|
||||
}
|
||||
Reference in New Issue
Block a user