feat: add services page

This commit is contained in:
jinhojang6 2023-11-10 01:38:54 +09:00
parent 7417b76851
commit bc24a3b197
13 changed files with 475 additions and 24 deletions

View File

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

View File

@ -29,6 +29,7 @@ const StyledButton = styled.button<ButtonProps>`
white-space: nowrap;
text-align: center;
border-radius: 2px;
position: relative;
&:hover {
opacity: 0.8;

View File

@ -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 {

View File

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

View File

@ -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`

View File

@ -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`

View File

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

View File

@ -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&apos;d love to hear from you if you&apos;re building or investing
in technologies to uphold users&apos; 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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,3 @@
export { default as ServiceFilter } from './ServiceFilter'
export { default as ServiceItem } from './ServiceItem'
export { default as ServiceList } from './ServiceList'

View File

@ -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 IFTs 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,
},
}
}