This commit is contained in:
Mihajlo Medjedovic
2024-06-05 15:53:46 +02:00
commit fd510b88c4
270 changed files with 27578 additions and 0 deletions

View File

@ -0,0 +1,75 @@
import React from 'react'
import { Link } from 'gatsby'
// import dcLogo from "../../images/dclogo.png";
import { Container, Section } from '../shared'
import { SolidButton } from '../shared/styledComponents'
import { StyledHeading, StyledDesc, InputStyled, StyledAnchor } from './style'
const anchorStyles = {
color: '#888'
}
const Footer = () => (
<Section bottomArrow={false}>
<div className="row">
<div className="col-md-3 me-md-5">
<StyledHeading>Data Controller</StyledHeading>
<StyledDesc>
Data Controller is a product of 4GL Apps, a brand of Bowe IO Ltd,
which is a UK company with a focus on SAS Software,{' '}
<StyledAnchor href="https://sasapps.io">Apps</StyledAnchor>, and
Services.
</StyledDesc>
</div>
<div className="col-md-3">
<StyledHeading>Newsletter</StyledHeading>
<form
className="kwes-form"
method="POST"
action="https://kwes.io/api/foreign/forms/mxKuyK4lxZWnG2WNH3ga"
>
<div className="mb-3">
<InputStyled
type="email"
name="email"
className="form-control"
aria-describedby="emailHelp"
placeholder="Email Address*"
required
/>
</div>
<div className="mb-3">
<InputStyled
type="text"
name="name"
className="form-control"
placeholder="First Name"
required
/>
</div>
<div className="mb-3">
<InputStyled
type="text"
name="lastName"
className="form-control"
placeholder="Last Name"
/>
</div>
<SolidButton>Subscribe</SolidButton>
</form>
</div>
<div className="col-md-3">
<StyledHeading>Other Resources</StyledHeading>
<StyledDesc>
Visit our educational and fun SAS® software quiz{' '}
<StyledAnchor href="https://sasensei.com">Sasensei</StyledAnchor> and
test your knowledge of SAS topics.
</StyledDesc>
</div>
</div>
</Section>
)
export default Footer

View File

@ -0,0 +1,40 @@
import React from 'react'
import styled from 'styled-components'
export const StyledHeading = styled.h6`
margin-bottom: 0.8rem;
text-transform: uppercase;
color: #888;
`
export const StyledDesc = styled.p`
color: #aaaaaa;
font-size: 0.9rem;
`
export const InputStyled = styled.input`
background: transparent;
border: none;
outline: none;
font-size: 0.9rem;
&:focus {
color: inherit;
background: transparent;
border: none;
outline: none;
box-shadow: none;
}
`
const Anchor = styled.a`
color: #d4d4d4;
text-decoration: none;
&:hover {
color: white;
}
`
export const StyledAnchor = ({ children, href }) => (
<Anchor href={href} target="_blank" rel="noopener">
{children}
</Anchor>
)

View File

@ -0,0 +1,37 @@
import React from 'react'
import { PageProps, Link } from 'gatsby'
import styled from 'styled-components'
import { Hero, HeroHeading, HeroDesc } from './style'
import { BottomSectionArrow, OutlineButton } from '../shared/styledComponents'
import { Container } from '../shared'
import { pathPrefix } from '../../../gatsby-config.js'
type DataProps = {
location: Location
heading: string
desc: string
}
const HeroSection: React.FC<PageProps<DataProps>> = ({
location,
heading,
desc
}) => (
<Hero bg={location.pathname === pathPrefix + '/'}>
<Container>
<HeroHeading>{heading}</HeroHeading>
<HeroDesc>{desc}</HeroDesc>
{location.pathname === pathPrefix + '/' && (
<Link to="/contact/">
<OutlineButton>Try Data Controller</OutlineButton>
</Link>
)}
</Container>
<BottomSectionArrow />
</Hero>
)
export default HeroSection

View File

@ -0,0 +1,23 @@
import styled from 'styled-components'
import background from '../../images/home_hero_bg.png'
export const Hero = styled.main`
position: relative;
padding: 50px 0;
color: white;
background-color: #314351;
background-repeat: no-repeat;
background-image: ${(props) => (props.bg ? `url(${background})` : 'none')};
background-attachment: scroll;
background-position: bottom right;
`
export const HeroHeading = styled.h1`
font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
sans-serif;
text-transform: uppercase;
`
export const HeroDesc = styled.p`
opacity: 0.8;
`

41
src/components/layout.tsx Normal file
View File

@ -0,0 +1,41 @@
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
}
const Layout: React.FC<PageProps<DataProps>> = ({
location,
children,
heroSection = true,
heading,
desc
}) => {
useEffect(() => {
if (document.querySelector('script[data-name="kwes-script"]')) return
const kwesScript = document.createElement('script')
kwesScript.setAttribute('rel', 'noopener')
kwesScript.setAttribute('src', 'https://kwes.io/v2/kwes-script.js')
kwesScript.setAttribute('data-name', 'kwes-script')
document.head.appendChild(kwesScript)
})
return (
<>
<Navibar location={location} />
{heroSection && (
<HeroSection location={location} heading={heading} desc={desc} />
)}
{children}
<Footer />
</>
)
}
export default Layout

View File

@ -0,0 +1,105 @@
import React from 'react'
import { Link, PageProps } 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'
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'
},
{
name: 'Documentation',
url: 'https://docs.datacontroller.io/',
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',
active: 'no'
}
]
type DataProps = {
location: Location
}
const Navibar: React.FC<PageProps<DataProps>> = ({ location }) => {
naviLinks.forEach((link) => (link.active = 'no'))
const currentLink = naviLinks.find(
(link) => pathPrefix + link.url === location?.pathname
)
if (currentLink) currentLink.active = 'yes'
return (
<CustomNavBar className="navbar navbar-expand-lg">
<Container>
<Link to="/">
<img src={dcLogo} style={logoStyles} alt="Data Controller Logo" />
</Link>
<button
className="navbar-toggler collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<div className="navbar-toggler-icon" id="nav-icon4">
<span></span>
<span></span>
<span></span>
</div>
</button>
<div className="collapse navbar-collapse" id="navbarSupportedContent">
<ul className="navbar-nav mb-2 mb-lg-0" style={ulStyles}>
{naviLinks.map((link, index) => (
<Li key={index} className="nav-item">
<StyledLink
to={link.url}
className="nav-link"
active={link.active}
>
{link.name}
</StyledLink>
</Li>
))}
</ul>
</div>
</Container>
</CustomNavBar>
)
}
export default Navibar

View File

@ -0,0 +1,333 @@
import React from 'react'
import styled, { css } from 'styled-components'
import { Link } from 'gatsby'
// styles
export const logoStyles = {
height: '55px'
}
export const ulStyles = {
marginLeft: 'auto'
}
export const CustomNavBar = styled.nav`
padding: 0;
background-color: #314351;
color: white;
font-size: 0.85rem;
/* Icon 1 */
#nav-icon1,
#nav-icon2,
#nav-icon3,
#nav-icon4 {
width: 60px;
height: 45px;
position: relative;
margin: 10px auto;
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
-webkit-transition: 0.5s ease-in-out;
-moz-transition: 0.5s ease-in-out;
-o-transition: 0.5s ease-in-out;
transition: 0.5s ease-in-out;
cursor: pointer;
}
#nav-icon1 span,
#nav-icon3 span,
#nav-icon4 span {
display: block;
position: absolute;
height: 9px;
width: 100%;
background: #79a843;
border-radius: 9px;
opacity: 1;
left: 0;
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
-webkit-transition: 0.25s ease-in-out;
-moz-transition: 0.25s ease-in-out;
-o-transition: 0.25s ease-in-out;
transition: 0.25s ease-in-out;
}
#nav-icon1 span:nth-child(1) {
top: 0px;
}
#nav-icon1 span:nth-child(2) {
top: 18px;
}
#nav-icon1 span:nth-child(3) {
top: 36px;
}
.navbar-toggler:not(.collapsed) #nav-icon1 span:nth-child(1) {
top: 18px;
-webkit-transform: rotate(135deg);
-moz-transform: rotate(135deg);
-o-transform: rotate(135deg);
transform: rotate(135deg);
}
.navbar-toggler:not(.collapsed) #nav-icon1 span:nth-child(2) {
opacity: 0;
left: -60px;
}
.navbar-toggler:not(.collapsed) #nav-icon1 span:nth-child(3) {
top: 18px;
-webkit-transform: rotate(-135deg);
-moz-transform: rotate(-135deg);
-o-transform: rotate(-135deg);
transform: rotate(-135deg);
}
/* Icon 2 */
#nav-icon2 {
}
#nav-icon2 span {
display: block;
position: absolute;
height: 9px;
width: 50%;
background: #d3531a;
opacity: 1;
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
-webkit-transition: 0.25s ease-in-out;
-moz-transition: 0.25s ease-in-out;
-o-transition: 0.25s ease-in-out;
transition: 0.25s ease-in-out;
}
#nav-icon2 span:nth-child(even) {
left: 50%;
border-radius: 0 9px 9px 0;
}
#nav-icon2 span:nth-child(odd) {
left: 0px;
border-radius: 9px 0 0 9px;
}
#nav-icon2 span:nth-child(1),
#nav-icon2 span:nth-child(2) {
top: 0px;
}
#nav-icon2 span:nth-child(3),
#nav-icon2 span:nth-child(4) {
top: 18px;
}
#nav-icon2 span:nth-child(5),
#nav-icon2 span:nth-child(6) {
top: 36px;
}
.navbar-toggler:not(.collapsed) #nav-icon2 span:nth-child(1),
.navbar-toggler:not(.collapsed) #nav-icon2 span:nth-child(6) {
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
}
.navbar-toggler:not(.collapsed) #nav-icon2 span:nth-child(2),
.navbar-toggler:not(.collapsed) #nav-icon2 span:nth-child(5) {
-webkit-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);
-o-transform: rotate(-45deg);
transform: rotate(-45deg);
}
.navbar-toggler:not(.collapsed) #nav-icon2 span:nth-child(1) {
left: 5px;
top: 7px;
}
.navbar-toggler:not(.collapsed) #nav-icon2 span:nth-child(2) {
left: calc(50% - 5px);
top: 7px;
}
.navbar-toggler:not(.collapsed) #nav-icon2 span:nth-child(3) {
left: -50%;
opacity: 0;
}
.navbar-toggler:not(.collapsed) #nav-icon2 span:nth-child(4) {
left: 100%;
opacity: 0;
}
.navbar-toggler:not(.collapsed) #nav-icon2 span:nth-child(5) {
left: 5px;
top: 29px;
}
.navbar-toggler:not(.collapsed) #nav-icon2 span:nth-child(6) {
left: calc(50% - 5px);
top: 29px;
}
/* Icon 3 */
#nav-icon3 span:nth-child(1) {
top: 0px;
}
#nav-icon3 span:nth-child(2),
#nav-icon3 span:nth-child(3) {
top: 18px;
}
#nav-icon3 span:nth-child(4) {
top: 36px;
}
.navbar-toggler:not(.collapsed) #nav-icon3 span:nth-child(1) {
top: 18px;
width: 0%;
left: 50%;
}
.navbar-toggler:not(.collapsed) #nav-icon3 span:nth-child(2) {
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
}
.navbar-toggler:not(.collapsed) #nav-icon3 span:nth-child(3) {
-webkit-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);
-o-transform: rotate(-45deg);
transform: rotate(-45deg);
}
.navbar-toggler:not(.collapsed) #nav-icon3 span:nth-child(4) {
top: 18px;
width: 0%;
left: 50%;
}
/* Icon 4 */
#nav-icon4 {
}
#nav-icon4 span:nth-child(1) {
top: 0px;
-webkit-transform-origin: left center;
-moz-transform-origin: left center;
-o-transform-origin: left center;
transform-origin: left center;
}
#nav-icon4 span:nth-child(2) {
top: 18px;
-webkit-transform-origin: left center;
-moz-transform-origin: left center;
-o-transform-origin: left center;
transform-origin: left center;
}
#nav-icon4 span:nth-child(3) {
top: 36px;
-webkit-transform-origin: left center;
-moz-transform-origin: left center;
-o-transform-origin: left center;
transform-origin: left center;
}
.navbar-toggler:not(.collapsed) #nav-icon4 span:nth-child(1) {
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
top: -3px;
left: 8px;
}
.navbar-toggler:not(.collapsed) #nav-icon4 span:nth-child(2) {
width: 0%;
opacity: 0;
}
.navbar-toggler:not(.collapsed) #nav-icon4 span:nth-child(3) {
-webkit-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);
-o-transform: rotate(-45deg);
transform: rotate(-45deg);
top: 39px;
left: 8px;
}
.navbar-toggler {
padding: 0;
&:focus {
box-shadow: none;
}
}
`
const LinkUnderlineStyles = css`
content: ' ';
position: absolute;
width: calc(100% - 1.6rem);
height: 2px;
bottom: 0;
background: white;
opacity: 1;
transition: opacity 0.3s ease;
`
// styled components
export const Li = styled.li`
position: relative;
@media (min-width: 992px) {
&:after {
content: ' ';
position: absolute;
width: 1px;
height: 50%;
top: 25%;
right: 0;
background: white;
}
&:nth-last-child(1) {
&:after {
display: none;
}
}
}
`
export const StyledLink = styled((props) => <Link {...props} />)`
padding-right: 0.8rem !important;
padding-left: 0.8rem !important;
color: white;
&:before {
${LinkUnderlineStyles}
opacity: ${(props) => (props.active === 'yes' ? '1' : 0)};
}
&:hover {
color: white;
&:before {
${LinkUnderlineStyles}
}
}
`

80
src/components/seo.tsx Normal file
View File

@ -0,0 +1,80 @@
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 }) => {
const { site } = useStaticQuery(graphql`
query {
site {
siteMetadata {
title
description
siteUrl
author {
name
}
social {
linkedin
}
}
}
}
`)
const author = site.siteMetadata?.author?.name
const metaDescription = description || site.siteMetadata.description
const defaultTitle = site.siteMetadata?.title
const pageTitle = title ? `${title} | ${defaultTitle}` : defaultTitle
const siteUrl = site.siteMetadata?.siteUrl
const image = previewImg
? `${siteUrl}${previewImg}`
: `${siteUrl}/img/data-controller.svg`
return (
<Helmet
htmlAttributes={{
lang
}}
title={pageTitle}
meta={[
{ name: 'author', property: 'author', content: author },
{
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)}
/>
)
}
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

View File

@ -0,0 +1,18 @@
import React from 'react'
import styled from 'styled-components'
import { PageProps } from 'gatsby'
type DataProps = {
children?: React.ReactNode
}
const StyledDiv = styled.div`
@media (min-width: 576px) {
max-width: 1310px;
padding: 0px 50px;
}
`
export const Container: React.FC<PageProps<DataProps>> = ({ children }) => {
return <StyledDiv className="container">{children}</StyledDiv>
}

View File

@ -0,0 +1,3 @@
export { Container } from './container'
export { Section } from './section'
export { ScheduleDemo } from './scheduleDemo'

View File

@ -0,0 +1,53 @@
import React from 'react'
import { Link } from 'gatsby'
import styled from 'styled-components'
import { FaEnvelope } from 'react-icons/fa'
export const StyledLink = styled((props) => <Link {...props} />)`
color: rgb(255, 255, 255);
background-color: rgb(144, 196, 69);
border-radius: 0px;
padding: 50px 10px;
width: 100%;
margin: 0px;
border: none;
position: relative;
display: block;
text-decoration: none;
font-size: 1.3rem;
line-height: 1.2em;
text-align: center;
max-width: 100%;
font-family: Montserrat, HelveticaNeue, 'Helvetica Neue', Helvetica, Arial;
svg {
opacity: 0;
transition: all 0.5s ease;
}
&:hover {
color: white;
background-color: #314351;
svg {
opacity: 1;
}
}
transition: all 0.3s ease;
`
const iconStyles = { marginTop: '-2px', marginLeft: '5px' }
const textStyles = { opacity: '0.7', fontSize: '1rem', margin: '12px auto 0' }
export const ScheduleDemo = () => {
return (
<StyledLink to="/contact">
<span>
Schedule a Free Demo <FaEnvelope size={18} style={iconStyles} />
</span>
<p style={textStyles}>
Contact us for a free demonstration of Data Controller.
</p>
</StyledLink>
)
}

View File

@ -0,0 +1,34 @@
import 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
}
const StyledSection = styled.div`
position: relative;
padding: 50px 0;
color: ${(props) => props.color || 'white'};
background-color: ${(props) => props.bgColor || '#314351'};
`
export const Section: React.FC<PageProps<DataProps>> = ({
children,
bgColor,
color,
bottomArrow = true
}) => {
return (
<StyledSection bgColor={bgColor} color={color}>
<Container>{children}</Container>
{bottomArrow && <BottomSectionArrow />}
</StyledSection>
)
}

View File

@ -0,0 +1,88 @@
import React from 'react'
import styled from 'styled-components'
const BottomArrow = styled.div`
width: 50px;
height: 50px;
position: absolute;
bottom: 10px;
background: inherit;
transform: translateX(-50%) rotate(45deg);
left: 50%;
// right: 0;
// margin-left: auto;
// margin-right: auto;
z-index: 10;
`
const BottomArrowWrapper = styled.div`
width: 100%;
height: 20px;
position: absolute;
overflow: hidden;
margin-top: 50px;
background-color: inherit;
`
export const BottomSectionArrow = () => (
<BottomArrowWrapper>
<BottomArrow />
</BottomArrowWrapper>
)
export const SectionHeading = styled.h2`
text-align: ${(props) => (props.center === 'no' ? 'left' : 'center')};
letter-spacing: 1px;
font-weight: 400;
font-family: 'Montserrat', 'HelveticaNeue', 'Helvetica Neue', Helvetica, Arial,
sans-serif;
text-transform: uppercase;
`
export const SectionDesc = styled.p`
text-align: ${(props) => (props.center === 'no' ? 'left' : 'center')};
opacity: ${(props) => props.opacity ?? 0.6};
a {
color: inherit;
}
`
const StyledSolidButton = styled.button`
padding: 0.75rem 1.5rem;
font-size: 0.75rem;
border-width: 2px;
width: 100%;
max-width: 250px;
&:hover {
opacity: 0.9;
}
&.btn-dark {
background-color: #2e4252;
}
`
export const SolidButton = ({
children,
theme = 'light',
type = 'submit',
onClick = undefined
}) => (
<StyledSolidButton
type={type}
className={`btn btn-${theme}`}
onClick={onClick}
>
{children}
</StyledSolidButton>
)
const StyledOutlineButton = styled.button`
margin: 50px 0;
padding: 0.75rem 1.5rem;
font-size: 0.75rem;
border-width: 2px;
`
export const OutlineButton = ({ children }) => (
<StyledOutlineButton type="button" className="btn btn-outline-light">
{children}
</StyledOutlineButton>
)