feat: add services page
This commit is contained in:
parent
7417b76851
commit
bc24a3b197
|
@ -66,7 +66,6 @@ function useFetchJobs() {
|
|||
(job) => job.title && job.title.includes(titleFilter),
|
||||
)
|
||||
}
|
||||
console.log(allJobs)
|
||||
setData({ jobs: allJobs, meta: { total: allJobs.length } })
|
||||
} catch (err: any) {
|
||||
setError(err)
|
||||
|
|
|
@ -29,6 +29,7 @@ const StyledButton = styled.button<ButtonProps>`
|
|||
white-space: nowrap;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
|
|
|
@ -48,7 +48,7 @@ export const Footer = () => {
|
|||
<h3>Infrastructure</h3>
|
||||
<LinkItems>
|
||||
{footerBUs.map((bu) => (
|
||||
<a key={bu.link} href={bu.link} target="_blank">
|
||||
<a key={bu.name} href={bu.link} target="_blank">
|
||||
{bu.name}
|
||||
</a>
|
||||
))}
|
||||
|
@ -58,7 +58,7 @@ export const Footer = () => {
|
|||
<h3>Social</h3>
|
||||
<LinkItems>
|
||||
{footerSocial.map((item) => (
|
||||
<a key={item.link} href={item.link} target="_blank">
|
||||
<a key={item.name} href={item.link} target="_blank">
|
||||
{item.name}
|
||||
</a>
|
||||
))}
|
||||
|
@ -70,14 +70,13 @@ export const Footer = () => {
|
|||
)
|
||||
}
|
||||
|
||||
const Container = styled.nav`
|
||||
const Container = styled.footer`
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
background-color: black;
|
||||
flex-direction: column;
|
||||
color: white;
|
||||
padding: 16px 16px 44px 16px;
|
||||
margin-top: 204px;
|
||||
|
||||
h2 {
|
||||
padding-block: 24px;
|
||||
|
@ -88,7 +87,6 @@ const Container = styled.nav`
|
|||
|
||||
@media (max-width: ${breakpoints.md}px) {
|
||||
width: 100%;
|
||||
margin-top: 74px;
|
||||
padding: 16px 8px 0 8px;
|
||||
|
||||
h2 {
|
||||
|
|
|
@ -32,7 +32,7 @@ export const HomeFooter = () => {
|
|||
<LinksColumn>
|
||||
<h3>Infrastructure</h3>
|
||||
{footerBUs.map((bu) => (
|
||||
<a key={bu.link} href={bu.link} target="_blank">
|
||||
<a key={bu.name} href={bu.link} target="_blank">
|
||||
{bu.name}
|
||||
</a>
|
||||
))}
|
||||
|
@ -40,7 +40,7 @@ export const HomeFooter = () => {
|
|||
<LinksColumn>
|
||||
<h3>Social</h3>
|
||||
{footerSocial.map((item) => (
|
||||
<a key={item.link} href={item.link} target="_blank">
|
||||
<a key={item.name} href={item.link} target="_blank">
|
||||
{item.name}
|
||||
</a>
|
||||
))}
|
||||
|
@ -57,7 +57,7 @@ export const HomeFooter = () => {
|
|||
)
|
||||
}
|
||||
|
||||
const Container = styled.nav`
|
||||
const Container = styled.footer`
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
background-color: black;
|
||||
|
|
|
@ -21,10 +21,8 @@ const JobFilter = ({ jobs, activeBUs, setActiveBUs }: Props) => {
|
|||
|
||||
const toggleBU = (bu: string) => {
|
||||
if (activeBUs.includes(bu)) {
|
||||
// If the bu is already active, remove it from activeBUs
|
||||
setActiveBUs((prevBUs) => prevBUs.filter((item) => item !== bu))
|
||||
} else {
|
||||
// Otherwise, add it to activeBUs
|
||||
setActiveBUs((prevBUs) => [...prevBUs, bu])
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +59,7 @@ const Container = styled.div`
|
|||
flex-direction: column;
|
||||
width: 100%;
|
||||
margin-top: calc(${uiConfigs.navbarHeight}px + 24px);
|
||||
margin-bottom: 56px;
|
||||
`
|
||||
|
||||
const Title = styled.h3`
|
||||
|
|
|
@ -17,7 +17,6 @@ export type Job = {
|
|||
}
|
||||
|
||||
const JobItem = ({ job }: { job: Job }) => {
|
||||
console.log(job)
|
||||
return (
|
||||
<JobContainer>
|
||||
<JobHeader>
|
||||
|
@ -86,7 +85,7 @@ const JobInfo = styled.p`
|
|||
const IconContainer = styled.span`
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 8px;
|
||||
right: 7px;
|
||||
`
|
||||
|
||||
const ApplyButton = styled.a`
|
||||
|
|
|
@ -39,10 +39,12 @@ const Container = styled.div`
|
|||
width: 100%;
|
||||
justify-content: space-between;
|
||||
margin-top: 180px;
|
||||
margin-bottom: 204px;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.18);
|
||||
|
||||
@media (max-width: ${breakpoints.md}px) {
|
||||
margin-top: 60px;
|
||||
margin-bottom: 74px;
|
||||
flex-direction: column;
|
||||
}
|
||||
`
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
import { breakpoints } from '@/configs/ui.configs'
|
||||
import styled from '@emotion/styled'
|
||||
|
||||
const ServiceContact = () => {
|
||||
return (
|
||||
<Container>
|
||||
<Header>Work with us!</Header>
|
||||
<Content>
|
||||
<Title>
|
||||
We'd love to hear from you if you're building or investing
|
||||
in technologies to uphold users' rights and freedoms.
|
||||
</Title>
|
||||
<Description>
|
||||
If our mission, the ideas we incubate, or our services interest you,
|
||||
please <a href="#">get in touch</a>.
|
||||
</Description>
|
||||
</Content>
|
||||
<EmailButton>Email us</EmailButton>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
margin-top: 268px;
|
||||
padding: 24px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
`
|
||||
|
||||
const Header = styled.div`
|
||||
display: flex;
|
||||
font-size: 22px;
|
||||
font-weight: 400;
|
||||
padding: 24px 0;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.18);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.18);
|
||||
margin-bottom: 24px;
|
||||
|
||||
@media (max-width: ${breakpoints.md}px) {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
`
|
||||
|
||||
const Content = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
margin-bottom: 56px;
|
||||
|
||||
@media (max-width: ${breakpoints.md}px) {
|
||||
gap: 8px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
`
|
||||
|
||||
const Title = styled.h3`
|
||||
font-size: 52px;
|
||||
color: black;
|
||||
|
||||
@media (max-width: ${breakpoints.md}px) {
|
||||
font-size: 22px;
|
||||
line-height: 122%;
|
||||
}
|
||||
`
|
||||
|
||||
const Description = styled.p`
|
||||
overflow: hidden;
|
||||
color: #000;
|
||||
font-size: 22px;
|
||||
line-height: 122%;
|
||||
font-weight: 400;
|
||||
max-width: 520px;
|
||||
|
||||
@media (max-width: ${breakpoints.md}px) {
|
||||
font-size: 12px;
|
||||
}
|
||||
`
|
||||
|
||||
const EmailButton = styled.button`
|
||||
height: 42px;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
border-radius: 2px;
|
||||
background: black;
|
||||
position: relative;
|
||||
color: black;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 10px 49px 10px 18px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
`
|
||||
|
||||
export default ServiceContact
|
|
@ -0,0 +1,128 @@
|
|||
import { breakpoints, uiConfigs } from '@/configs/ui.configs'
|
||||
import styled from '@emotion/styled'
|
||||
import { ServiceType } from './ServiceItem'
|
||||
|
||||
type Props = {
|
||||
services: ServiceType[]
|
||||
activeServices: string[]
|
||||
setActiveServices: React.Dispatch<React.SetStateAction<string[]>>
|
||||
}
|
||||
|
||||
const ServiceFilter = ({
|
||||
services,
|
||||
activeServices,
|
||||
setActiveServices,
|
||||
}: Props) => {
|
||||
if (services == null) {
|
||||
return <div>Something went wrong</div>
|
||||
}
|
||||
|
||||
const toggleBU = (bu: string) => {
|
||||
if (activeServices.includes(bu)) {
|
||||
setActiveServices((prevServicesContainer) =>
|
||||
prevServicesContainer.filter((item) => item !== bu),
|
||||
)
|
||||
} else {
|
||||
setActiveServices((prevServicesContainer) => [
|
||||
...prevServicesContainer,
|
||||
bu,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Title>Our Services</Title>
|
||||
<Border />
|
||||
<ServicesContainer>
|
||||
{services?.length ? (
|
||||
services.map((service) => (
|
||||
<Tag
|
||||
active={activeServices.includes(service.title)}
|
||||
key={service.title + '-tag'}
|
||||
onClick={() => toggleBU(service.title)}
|
||||
>
|
||||
{service.title}
|
||||
</Tag>
|
||||
))
|
||||
) : (
|
||||
<NoServices>No Open Positions</NoServices>
|
||||
)}
|
||||
</ServicesContainer>
|
||||
<Border />
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
margin-top: calc(${uiConfigs.navbarHeight}px + 24px);
|
||||
margin-bottom: 56px;
|
||||
`
|
||||
|
||||
const Title = styled.h3`
|
||||
color: #000;
|
||||
font-size: 52px;
|
||||
font-weight: 400;
|
||||
line-height: 130%; /* 67.6px */
|
||||
text-transform: capitalize;
|
||||
margin-bottom: 24px;
|
||||
`
|
||||
|
||||
const ServicesContainer = styled.div`
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
gap: 16px;
|
||||
padding: 16px 0;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: ${breakpoints.md}px) {
|
||||
width: calc(100vw - 32px);
|
||||
margin-left: -16px;
|
||||
padding: 16px;
|
||||
}
|
||||
`
|
||||
|
||||
const Border = styled.hr`
|
||||
background: rgba(0, 0, 0, 0.18);
|
||||
border: 0;
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
`
|
||||
|
||||
const Tag = styled.div<{ active: boolean }>`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: ${({ active }) => (active ? 'black' : 'white')};
|
||||
color: ${({ active }) => (active ? 'white' : 'black')};
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
height: 28px;
|
||||
border-radius: 14px;
|
||||
padding: 4px 14px;
|
||||
box-sizing: border-box;
|
||||
text-transform: capitalize;
|
||||
cursor: pointer;
|
||||
border: 1px solid black;
|
||||
white-space: nowrap;
|
||||
|
||||
@media (max-width: ${breakpoints.md}px) {
|
||||
padding: 4px 10px;
|
||||
}
|
||||
`
|
||||
|
||||
const NoServices = styled.p`
|
||||
padding-top: 24px;
|
||||
font-size: 36px;
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
`
|
||||
|
||||
export default ServiceFilter
|
|
@ -0,0 +1,99 @@
|
|||
import { breakpoints } from '@/configs/ui.configs'
|
||||
import styled from '@emotion/styled'
|
||||
import Link from 'next/link'
|
||||
import ArrowUpRight from '../Icons/ArrowUpRight'
|
||||
|
||||
export type ServiceType = {
|
||||
id: string
|
||||
title: string
|
||||
description: string
|
||||
button?: {
|
||||
text: string
|
||||
url: string
|
||||
}
|
||||
}
|
||||
|
||||
const ServiceItem = ({ service }: { service: ServiceType }) => {
|
||||
return (
|
||||
<ServiceContainer>
|
||||
<ServiceHeader>
|
||||
<Description>{service.description}</Description>
|
||||
</ServiceHeader>
|
||||
{service?.button && (
|
||||
<ServiceButtonLink
|
||||
href={service.button.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<ServiceButton>
|
||||
{service.button.text}
|
||||
<IconContainer>
|
||||
<ArrowUpRight />
|
||||
</IconContainer>
|
||||
</ServiceButton>
|
||||
</ServiceButtonLink>
|
||||
)}
|
||||
</ServiceContainer>
|
||||
)
|
||||
}
|
||||
|
||||
const ServiceContainer = styled.div`
|
||||
padding: 24px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.18);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
`
|
||||
|
||||
const ServiceHeader = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
margin-bottom: 32px;
|
||||
|
||||
@media (max-width: ${breakpoints.md}px) {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
`
|
||||
|
||||
const Description = styled.p`
|
||||
overflow: hidden;
|
||||
color: #000;
|
||||
font-size: 22px;
|
||||
line-height: 122%;
|
||||
font-weight: 400;
|
||||
|
||||
@media (max-width: ${breakpoints.md}px) {
|
||||
font-size: 12px;
|
||||
}
|
||||
`
|
||||
|
||||
const ServiceButtonLink = styled(Link)`
|
||||
text-decoration: none;
|
||||
`
|
||||
|
||||
const ServiceButton = styled.button`
|
||||
height: 42px;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
border-radius: 2px;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
position: relative;
|
||||
color: black;
|
||||
padding: 10px 49px 10px 18px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
`
|
||||
|
||||
const IconContainer = styled.span`
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 7px;
|
||||
`
|
||||
|
||||
export default ServiceItem
|
|
@ -0,0 +1,69 @@
|
|||
import { breakpoints } from '@/configs/ui.configs'
|
||||
import styled from '@emotion/styled'
|
||||
import ServiceItem, { ServiceType } from './ServiceItem' // adjust path accordingly
|
||||
|
||||
type Props = {
|
||||
services: ServiceType[]
|
||||
activeServices: string[]
|
||||
}
|
||||
|
||||
const ServiceList = ({ services, activeServices }: Props) => {
|
||||
if (services == null) {
|
||||
return <div>Something went wrong</div>
|
||||
}
|
||||
|
||||
return services
|
||||
.filter((service) =>
|
||||
!activeServices?.length ? true : activeServices.includes(service.title),
|
||||
)
|
||||
.map((service, idx) => (
|
||||
<Container key={idx + '-services'}>
|
||||
<TitleContainer>
|
||||
<Title>{service.title}</Title>
|
||||
</TitleContainer>
|
||||
<Service>
|
||||
<ServiceItem key={service.id} service={service} />
|
||||
</Service>
|
||||
</Container>
|
||||
))
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
margin-top: 180px;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.18);
|
||||
|
||||
@media (max-width: ${breakpoints.md}px) {
|
||||
margin-top: 60px;
|
||||
flex-direction: column;
|
||||
}
|
||||
`
|
||||
|
||||
const Service = styled.div`
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const TitleContainer = styled.div`
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const Title = styled.h3`
|
||||
padding-top: 24px;
|
||||
width: 360px;
|
||||
color: #000;
|
||||
font-size: 52px;
|
||||
font-weight: 400;
|
||||
line-height: 60px;
|
||||
text-transform: capitalize;
|
||||
|
||||
@media (max-width: ${breakpoints.md}px) {
|
||||
font-size: 22px;
|
||||
line-height: 122%;
|
||||
padding-block: 16px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
`
|
||||
|
||||
export default ServiceList
|
|
@ -0,0 +1,3 @@
|
|||
export { default as ServiceFilter } from './ServiceFilter'
|
||||
export { default as ServiceItem } from './ServiceItem'
|
||||
export { default as ServiceList } from './ServiceList'
|
|
@ -1,27 +1,80 @@
|
|||
import { Box } from '@/components/Box'
|
||||
import { SEO } from '@/components/SEO'
|
||||
import { DefaultLayout } from '../layouts/DefaultLayout'
|
||||
import { ServiceFilter, ServiceList } from '@/components/Services'
|
||||
import ServiceContact from '@/components/Services/ServiceContact'
|
||||
import { ServiceType } from '@/components/Services/ServiceItem'
|
||||
import { SubPageLayout } from '@/layouts/SubPageLayout'
|
||||
import { useState } from 'react'
|
||||
|
||||
const Page = ({ services }: any) => {
|
||||
const [activeServices, setActiveServices] = useState<string[]>([])
|
||||
|
||||
const Page = () => {
|
||||
return (
|
||||
<>
|
||||
<SEO />
|
||||
<div>
|
||||
<Box>
|
||||
<ServiceFilter
|
||||
services={services}
|
||||
activeServices={activeServices}
|
||||
setActiveServices={setActiveServices}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<ServiceList services={services} activeServices={activeServices} />
|
||||
<ServiceContact />
|
||||
</Box>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Page.getLayout = function getLayout(page: React.ReactNode) {
|
||||
return <DefaultLayout>{page}</DefaultLayout>
|
||||
return <SubPageLayout>{page}</SubPageLayout>
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
try {
|
||||
return {
|
||||
props: {},
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
props: {},
|
||||
}
|
||||
const services: ServiceType[] = [
|
||||
{
|
||||
id: '1',
|
||||
title: 'Technical',
|
||||
description:
|
||||
"Vac is a principle-driven research and development group that provides technical support to each IFT startup. It comprises elite specialists in distributed systems, zero-knowledge proofs, and P2P networking, produces specifications for IFT startups, and undertakes comprehensive protocol testing. Like the IFT and the startups with which it works, a commitment to liberty, censorship resistance, security, privacy, decentralisation, and inclusivity guides Vac's R&D efforts.",
|
||||
button: {
|
||||
text: 'Discover Vac',
|
||||
url: 'https://vac.dev/',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'Financial',
|
||||
description:
|
||||
'Our startups benefit from IFT capital to fund their research and development efforts. Meanwhile, the IFT nurtures relationships with investors who share our values and commitment to world-changing tech. Besides funding and investment, the IFT handles compensation, bonus payments, and expenses incurred for all contributors working with us via our in-house finance department.',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: 'Legal',
|
||||
description:
|
||||
'A specialist and highly experienced legal department led by Dr Agata Ferreira helps our startups navigate the often confusing and ever-changing blockchain legal landscape. Supporting Agata is a team of two with backgrounds in crypto finance and blockchain legal issues. The department ensures our startups remain sound from a legal perspective.',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
title: 'HR',
|
||||
description:
|
||||
'Our PeopleOps department enables contributors across the IFT to focus on developing and supporting the innovations we incubate. The team of seven handles talent scouting, recruitment, onboarding, travel, career development, administrative support, and other initiatives to keep those working with us happy, engaged, and productive.',
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
title: 'Branding and awareness',
|
||||
description:
|
||||
'Ned Karlovich draws on experience as Creative Director at Nike Inc. and staat to lead the IFT’s creative branding studio, Acid. The studio works with our startups to deploy high-impact campaigns, raising brand awareness among their target markets both within the web3 ecosystem and beyond it. IFT startups enjoy full access to the studio, which comprises marketing, digital communications, design, web development, and events management specialists.',
|
||||
},
|
||||
]
|
||||
|
||||
return {
|
||||
props: {
|
||||
services,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue