[website] Add Jobs page (#433)
* add jobs route * implement jobs page * imlement jobs detail page * remove vertical padding from feature list * fix field * fix button pressing * fix feature list * fix tag --------- Co-authored-by: Jakub Kotula <520927+jkbktl@users.noreply.github.com>
This commit is contained in:
parent
6dd97428c7
commit
cadab15871
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
|
@ -7,7 +7,7 @@ import type { illustrations } from '@/config/illustrations'
|
||||||
type Item = {
|
type Item = {
|
||||||
title: string
|
title: string
|
||||||
description: string
|
description: string
|
||||||
image: (typeof illustrations)[keyof typeof illustrations]
|
icon: (typeof illustrations)[keyof typeof illustrations]
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -18,15 +18,15 @@ type Props = {
|
||||||
export const FeatureList = (props: Props) => {
|
export const FeatureList = (props: Props) => {
|
||||||
const { list, dark = false } = props
|
const { list, dark = false } = props
|
||||||
return (
|
return (
|
||||||
<div className="container pb-24 pt-12 lg:m-auto lg:gap-10">
|
<div className="container lg:m-auto lg:gap-10">
|
||||||
<div className="grid grid-cols-1 gap-12 sm:grid-cols-2 lg:-ml-10 lg:grid-cols-3">
|
<div className="grid grid-cols-1 gap-12 sm:grid-cols-2 lg:-ml-10 lg:grid-cols-3">
|
||||||
{list.map(({ title, image, description }, i) => (
|
{list.map(({ title, icon, description }, i) => (
|
||||||
<div key={title} className="flex flex-col">
|
<div key={title} className="flex flex-col">
|
||||||
<Image {...image} alt={image.alt} className="mb-4 ml-10" />
|
<Image {...icon} alt={icon.alt} className="mb-4 ml-10" />
|
||||||
<div
|
<div
|
||||||
className={cx(
|
className={cx(
|
||||||
'flex flex-col border-dashed lg:border-l-[1px] lg:pl-10',
|
'flex flex-col border-dashed lg:pl-10',
|
||||||
(i === 0 || i === 3) && 'lg:border-l-0',
|
i % 3 === 0 ? 'lg:border-l-0' : 'lg:border-l-[1px]',
|
||||||
dark ? 'border-l-neutral-70' : 'border-l-neutral-30'
|
dark ? 'border-l-neutral-70' : 'border-l-neutral-30'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
|
@ -38,7 +38,7 @@ export const ROUTES = {
|
||||||
{ name: 'Blog', href: '/blog' },
|
{ name: 'Blog', href: '/blog' },
|
||||||
{ name: 'Translations', href: '/' },
|
{ name: 'Translations', href: '/' },
|
||||||
// { name: 'Community groups', href: '/' },
|
// { name: 'Community groups', href: '/' },
|
||||||
{ name: 'Jobs', href: '/' },
|
{ name: 'Jobs', href: '/jobs' },
|
||||||
],
|
],
|
||||||
Developers: [
|
Developers: [
|
||||||
{ name: 'Repos', href: 'https://github.com/status-im' },
|
{ name: 'Repos', href: 'https://github.com/status-im' },
|
||||||
|
|
|
@ -35,37 +35,37 @@ const FEATURE_LIST = [
|
||||||
title: 'Decentralized',
|
title: 'Decentralized',
|
||||||
description:
|
description:
|
||||||
'Communities are literally powered by their members running the Status Desktop app.',
|
'Communities are literally powered by their members running the Status Desktop app.',
|
||||||
image: illustrations.doge,
|
icon: illustrations.doge,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Permissionless',
|
title: 'Permissionless',
|
||||||
description:
|
description:
|
||||||
'Nobody can stop you creating a community, because nobody controls Status’ p2p network.',
|
'Nobody can stop you creating a community, because nobody controls Status’ p2p network.',
|
||||||
image: illustrations.mushroom,
|
icon: illustrations.mushroom,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Self-sovereign',
|
title: 'Self-sovereign',
|
||||||
description:
|
description:
|
||||||
'Each community can set it’s own rules, whatever they are. And is responsible for it’s own actions. ',
|
'Each community can set it’s own rules, whatever they are. And is responsible for it’s own actions. ',
|
||||||
image: illustrations.hand,
|
icon: illustrations.hand,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '100% Free to use',
|
title: '100% Free to use',
|
||||||
description:
|
description:
|
||||||
'No paid tier. No artificially imposed limits. No commission charged on community token sales.',
|
'No paid tier. No artificially imposed limits. No commission charged on community token sales.',
|
||||||
image: illustrations.duck,
|
icon: illustrations.duck,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '100% Open source',
|
title: '100% Open source',
|
||||||
description:
|
description:
|
||||||
'Status itself is a community project. Anyone can build, contribute to and fork it’s source code.',
|
'Status itself is a community project. Anyone can build, contribute to and fork it’s source code.',
|
||||||
image: illustrations.flower,
|
icon: illustrations.flower,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '100% Freedom',
|
title: '100% Freedom',
|
||||||
description:
|
description:
|
||||||
'Status’s mission is to protect free speech, uphold human rights and defend privacy.',
|
'Status’s mission is to protect free speech, uphold human rights and defend privacy.',
|
||||||
image: illustrations.megaphone,
|
icon: illustrations.megaphone,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,154 @@
|
||||||
|
import { Button, Tag, Text } from '@status-im/components'
|
||||||
|
import { cx } from 'class-variance-authority'
|
||||||
|
|
||||||
|
import { Breadcrumbs } from '@/components/breadcrumbs'
|
||||||
|
import { AppLayout, Content } from '@/layouts/app-layout'
|
||||||
|
|
||||||
|
import type { BreadcrumbsProps } from '@/components/breadcrumbs'
|
||||||
|
import type { GetStaticPaths, GetStaticProps, Page } from 'next'
|
||||||
|
import type React from 'react'
|
||||||
|
|
||||||
|
type Params = { slug: string }
|
||||||
|
|
||||||
|
const SLUG = 'senior-react-native-ui-developer'
|
||||||
|
|
||||||
|
export const getStaticPaths: GetStaticPaths<Params> = async () => {
|
||||||
|
return {
|
||||||
|
paths: [
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
slug: SLUG,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fallback: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getStaticProps: GetStaticProps<Props, Params> = async () => {
|
||||||
|
// root
|
||||||
|
const breadcrumbs = [
|
||||||
|
{
|
||||||
|
label: 'Jobs',
|
||||||
|
// TODO: typesafe
|
||||||
|
href: '/jobs',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Senior React Native UI Developer',
|
||||||
|
href: `/jobs/${SLUG}`,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
breadcrumbs,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
breadcrumbs: BreadcrumbsProps['items']
|
||||||
|
}
|
||||||
|
|
||||||
|
const JobsDetailPage: Page<Props> = props => {
|
||||||
|
const { breadcrumbs } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Content>
|
||||||
|
<Breadcrumbs items={breadcrumbs} />
|
||||||
|
|
||||||
|
<div className="mx-auto max-w-[742px] px-5">
|
||||||
|
<div className="flex flex-col items-start gap-4 pb-6 pt-12 lg:pt-20">
|
||||||
|
<Tag size={24} label="Mobile" />
|
||||||
|
<h1 className="text-40 lg:text-64">
|
||||||
|
Senior React Native UI Developer
|
||||||
|
</h1>
|
||||||
|
<Text size={19} color="$neutral-50">
|
||||||
|
Full time, Remote (Worldwide)
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* CONTENT */}
|
||||||
|
<div className="pb-12 pt-6 lg:pb-20">
|
||||||
|
<Text size={19}>
|
||||||
|
The role We’re growing our mobile development team. Join us in
|
||||||
|
building a fully decentralized, censorship resistant, privacy first
|
||||||
|
group chat platform that leverages the Ethereum blockchain to enable
|
||||||
|
individuals and groups worldwide to communicate and transact with
|
||||||
|
one another freely without restriction. Status is looking for an UI
|
||||||
|
Engineer to join our mobile development team who will work closely
|
||||||
|
with Design to transform UI specifications into beautiful, smooth,
|
||||||
|
performant and near pixel perfect interactive interfaces. The ideal
|
||||||
|
person will be comfortable working on features end-to-end, has an
|
||||||
|
eye for design and visual detail, enjoys working with designers as
|
||||||
|
well as developers, and who can transform reused UI patterns such as
|
||||||
|
list items into reusable UI components.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The ideal candidate will love finessing UI implementations to the
|
||||||
|
highest levels of quality and will care deeply about things like
|
||||||
|
code cleanliness, reusability, maintainability, performance, and UI
|
||||||
|
layout accuracy - as well as doing whatever needed to create a
|
||||||
|
best-in-class user experience for Status’s users. Willingness to
|
||||||
|
both learn and share your knowledge with others,
|
||||||
|
product-orientation, and making sure all team members are aligned
|
||||||
|
when developing new features, will make you successful in the role.
|
||||||
|
Status is a fast-paced, flat organization, working on cutting edge
|
||||||
|
blockchain and decentralized messaging technologies in a dynamic
|
||||||
|
landscape. We look forward to meeting you!
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text size={19}>
|
||||||
|
The role We’re growing our mobile development team. Join us in
|
||||||
|
building a fully decentralized, censorship resistant, privacy first
|
||||||
|
group chat platform that leverages the Ethereum blockchain to enable
|
||||||
|
individuals and groups worldwide to communicate and transact with
|
||||||
|
one another freely without restriction. Status is looking for an UI
|
||||||
|
Engineer to join our mobile development team who will work closely
|
||||||
|
with Design to transform UI specifications into beautiful, smooth,
|
||||||
|
performant and near pixel perfect interactive interfaces. The ideal
|
||||||
|
person will be comfortable working on features end-to-end, has an
|
||||||
|
eye for design and visual detail, enjoys working with designers as
|
||||||
|
well as developers, and who can transform reused UI patterns such as
|
||||||
|
list items into reusable UI components.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The ideal candidate will love finessing UI implementations to the
|
||||||
|
highest levels of quality and will care deeply about things like
|
||||||
|
code cleanliness, reusability, maintainability, performance, and UI
|
||||||
|
layout accuracy - as well as doing whatever needed to create a
|
||||||
|
best-in-class user experience for Status’s users. Willingness to
|
||||||
|
both learn and share your knowledge with others,
|
||||||
|
product-orientation, and making sure all team members are aligned
|
||||||
|
when developing new features, will make you successful in the role.
|
||||||
|
Status is a fast-paced, flat organization, working on cutting edge
|
||||||
|
blockchain and decentralized messaging technologies in a dynamic
|
||||||
|
landscape. We look forward to meeting you!
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* FOOTER */}
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
'border-dashed-default -mx-5 flex flex-col items-start gap-4 border-t px-5 pb-24 pt-6',
|
||||||
|
'md:mx-0 md:flex-row md:items-center md:justify-between md:px-0 md:pb-40'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<Text size={19} weight="semibold">
|
||||||
|
Apply for this job
|
||||||
|
</Text>
|
||||||
|
<Text size={15}>Submit your application here</Text>
|
||||||
|
</div>
|
||||||
|
<Button size={32}>Apply now</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Content>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
JobsDetailPage.getLayout = function getLayout(page) {
|
||||||
|
return <AppLayout>{page}</AppLayout>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default JobsDetailPage
|
|
@ -0,0 +1,276 @@
|
||||||
|
import { useRef } from 'react'
|
||||||
|
|
||||||
|
// import codexImage from '@assets/jobs/codex.png'
|
||||||
|
import logosImage from '@assets/jobs/logos.png'
|
||||||
|
// import nimbusImage from '@assets/jobs/nimbus.png'
|
||||||
|
// import vacImage from '@assets/jobs/vac.png'
|
||||||
|
// import wakuImage from '@assets/jobs/waku.png'
|
||||||
|
import { Button, Tag, Text } from '@status-im/components'
|
||||||
|
import { ArrowDownIcon, ArrowRightIcon, ExternalIcon } from '@status-im/icons'
|
||||||
|
import Image from 'next/image'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
|
import { FeatureList } from '@/components/feature-list'
|
||||||
|
import { illustrations } from '@/config/illustrations'
|
||||||
|
import { AppLayout, Content } from '@/layouts/app-layout'
|
||||||
|
|
||||||
|
import type { Page } from 'next'
|
||||||
|
|
||||||
|
// TODO FIX PHOTOS
|
||||||
|
// TODO FIX FEATURE LIST
|
||||||
|
// TODO ADD CIRCLES
|
||||||
|
// TODO CONNECT TO GREENHOUSE API (https://developers.greenhouse.io/job-board.html#retrieve-job-board)
|
||||||
|
|
||||||
|
const OPEN_JOBS = 82
|
||||||
|
|
||||||
|
const JobsPage: Page = () => {
|
||||||
|
const openRolesRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
const handleScrollToOpenings = () => {
|
||||||
|
openRolesRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Content>
|
||||||
|
<div className="container flex flex-col items-start pb-6 pt-16 lg:pb-20 lg:pt-40">
|
||||||
|
<div className="mb-4">
|
||||||
|
<Tag size={32} label="Jobs" />
|
||||||
|
</div>
|
||||||
|
<h1 className="mb-6 text-48 lg:mb-8 lg:text-88">
|
||||||
|
Join the
|
||||||
|
<br />
|
||||||
|
decentralized
|
||||||
|
<br />
|
||||||
|
movement
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div className="flex w-full max-w-[462px] flex-col justify-between gap-3 rounded-2xl border border-dashed border-neutral-80/20 p-3 md:flex-row md:gap-10 lg:items-center lg:p-2 lg:pl-3">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<Text size={13} weight="semibold">
|
||||||
|
We’re hiring!
|
||||||
|
</Text>
|
||||||
|
<Text size={13}>{OPEN_JOBS} remote openings</Text>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
iconAfter={<ArrowDownIcon size={20} />}
|
||||||
|
onPress={handleScrollToOpenings}
|
||||||
|
>
|
||||||
|
View openings
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* PHOTOS */}
|
||||||
|
<div className="container grid grid-cols-2 gap-4 pb-12 pt-6 lg:pb-20 lg:pt-10">
|
||||||
|
<img
|
||||||
|
src="https://images.pexels.com/photos/16972528/pexels-photo-16972528/free-photo-of-sea-flight-bird-beach.jpeg?auto=compress&cs=tinysrgb&w=750&h=750&dpr=2"
|
||||||
|
className="aspect-square rounded-3xl object-cover"
|
||||||
|
/>
|
||||||
|
<div className="grid grid-rows-2 gap-4">
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<img
|
||||||
|
src="https://images.pexels.com/photos/17162015/pexels-photo-17162015/free-photo-of-street-field-school-park.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
|
||||||
|
className="aspect-square rounded-3xl object-cover"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
src="https://images.pexels.com/photos/17162015/pexels-photo-17162015/free-photo-of-street-field-school-park.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
|
||||||
|
className="aspect-square rounded-3xl object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
src="https://images.pexels.com/photos/17162015/pexels-photo-17162015/free-photo-of-street-field-school-park.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
|
||||||
|
className="aspect-[2.060] rounded-3xl object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section className="border-b border-dashed border-neutral-80/20 pb-24 pt-12 lg:pb-40 lg:pt-20">
|
||||||
|
<div className="mx-auto max-w-[740px] px-5">
|
||||||
|
<Text size={19}>
|
||||||
|
Status strives to develop open source software that can be used as a
|
||||||
|
secure communication tool that upholds human rights. We are building
|
||||||
|
the tools and infrastructure for the advancement of a secure,
|
||||||
|
private, and open web3. Our core software, Status, is an open
|
||||||
|
source, Ethereum-based software that gives users the power to chat,
|
||||||
|
transact, and access a revolutionary world of DApps on the
|
||||||
|
decentralized web. With the high level goals of preserving the right
|
||||||
|
to privacy, protecting messages from third parties, and safely
|
||||||
|
sending, storing and receiving cryptocurrencies with you being the
|
||||||
|
only one who holds the keys to your funds, Status is providing the
|
||||||
|
tools and infrastructure to facilitate the creation of communities,
|
||||||
|
where anyone is welcome to create, join and contribute.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
150+ passionate individuals (known as core-contributors) work
|
||||||
|
together all around the world to facilitate Status’ mission. We care
|
||||||
|
deeply about our mission, so we have created an environment which
|
||||||
|
allows core-contributors to complete their work with freedom and
|
||||||
|
flexibility. We regularly survey the core contributors to gather
|
||||||
|
insights on their satisfaction with contributing to Status & the
|
||||||
|
most common answers regarding why they’re satisfied are: our
|
||||||
|
mission, values, flexibility, friendly environment, non-corporate
|
||||||
|
culture and our entrepreneurial spirit.
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div className="border-b border-dashed border-neutral-80/20 py-24 lg:py-40">
|
||||||
|
<div className="container mb-12 lg:mb-20">
|
||||||
|
<h2 className="text-40 lg:text-64">Perks and benefits</h2>
|
||||||
|
</div>
|
||||||
|
<FeatureList list={PERKS} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="container py-24 lg:py-40" ref={openRolesRef}>
|
||||||
|
<div className="mb-12 lg:mb-20">
|
||||||
|
<h2 className="text-40 lg:text-64">Open roles</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-12 lg:gap-16">
|
||||||
|
{/* JOB GROUPS */}
|
||||||
|
{[1, 2, 3].map(i => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className="border-b border-dashed border-neutral-80/20"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<h3 className="mb-3 text-27">Design</h3>
|
||||||
|
</div>
|
||||||
|
<div className="divide-y divide-dashed divide-neutral-80/20">
|
||||||
|
{[
|
||||||
|
'Product designer',
|
||||||
|
'Visual designer',
|
||||||
|
'Motion designer',
|
||||||
|
'Illustrator',
|
||||||
|
].map(position => (
|
||||||
|
// TODO: discuss hover state
|
||||||
|
<Link
|
||||||
|
href="/jobs/senior-react-native-ui-developer"
|
||||||
|
key={position}
|
||||||
|
className="flex items-center justify-between py-5"
|
||||||
|
>
|
||||||
|
<Text size={19} weight="semibold">
|
||||||
|
{position}
|
||||||
|
</Text>
|
||||||
|
<div className="flex items-center gap-6">
|
||||||
|
<Text size={19}>Full-time</Text>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size={40}
|
||||||
|
icon={<ArrowRightIcon size={20} />}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* TODO FIX TEXTURE */}
|
||||||
|
<div className="rounded-[40px] bg-[#F5F6FD]">
|
||||||
|
<div className="container py-24 lg:py-40">
|
||||||
|
<div className="mb-20">
|
||||||
|
<h3 className="text-40">
|
||||||
|
Open roles
|
||||||
|
<br />
|
||||||
|
in our network
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-12">
|
||||||
|
{['Logos', 'Codex', 'Waku', 'Nimbus', 'Vac'].map(name => (
|
||||||
|
<div key={name} className="grid gap-4">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Image
|
||||||
|
src={logosImage}
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
alt={`${name} logo`}
|
||||||
|
/>
|
||||||
|
<Text size={19} weight="semibold">
|
||||||
|
{name}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
{[1, 2, 3, 4, 5, 6].map(i => (
|
||||||
|
<Link
|
||||||
|
key={i}
|
||||||
|
href="https://google.com"
|
||||||
|
className="group flex items-center"
|
||||||
|
>
|
||||||
|
<Text size={15} weight="medium">
|
||||||
|
Protocol Research Engineer
|
||||||
|
</Text>
|
||||||
|
<ExternalIcon
|
||||||
|
size={16}
|
||||||
|
className="transition-transform group-hover:translate-x-[2px] group-hover:translate-y-[-2px]"
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Content>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const PERKS = [
|
||||||
|
{
|
||||||
|
title: 'Fully remote',
|
||||||
|
description: 'Work from wherever you want, whenever you want. ',
|
||||||
|
icon: illustrations.doge,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Unlimited vacation',
|
||||||
|
description:
|
||||||
|
'Take all the time off you need. We need you in your best self.',
|
||||||
|
icon: illustrations.doge,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Paid team offsites',
|
||||||
|
description: 'We meet at least once a year somewhere across the world. ',
|
||||||
|
icon: illustrations.doge,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Hardware stipend',
|
||||||
|
description: 'Need a laptop to work? We got you covered.',
|
||||||
|
icon: illustrations.doge,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Co-working stipend',
|
||||||
|
description:
|
||||||
|
'We offer a $250 stipend for you to work from a co-working space.',
|
||||||
|
icon: illustrations.doge,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Get paid in crypto',
|
||||||
|
description: 'We offer to pay salaries in SNT, ETH, USDC or fiat.',
|
||||||
|
icon: illustrations.doge,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'SNT Bonus',
|
||||||
|
description: 'We reward hard work and longevity with a bonus in SNT. ',
|
||||||
|
icon: illustrations.doge,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Referral fee',
|
||||||
|
description:
|
||||||
|
'We offer a $5000 SNT referral fee so bring your friends aboard.',
|
||||||
|
icon: illustrations.doge,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Headspace subscription',
|
||||||
|
description:
|
||||||
|
'Take care of your mind with a premium headspace account, for free.',
|
||||||
|
icon: illustrations.doge,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
JobsPage.getLayout = function getLayout(page) {
|
||||||
|
return <AppLayout>{page}</AppLayout>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default JobsPage
|
|
@ -132,36 +132,36 @@ const FEATURE_LIST = [
|
||||||
title: 'Top tier security',
|
title: 'Top tier security',
|
||||||
description:
|
description:
|
||||||
'Our hardware security successfully passed Common Criteria EAL6+ certification ',
|
'Our hardware security successfully passed Common Criteria EAL6+ certification ',
|
||||||
image: illustrations.doge,
|
icon: illustrations.doge,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Keys stored in the card',
|
title: 'Keys stored in the card',
|
||||||
description:
|
description:
|
||||||
'It’s impossible for Status or any government to extract your private keys from keycard',
|
'It’s impossible for Status or any government to extract your private keys from keycard',
|
||||||
image: illustrations.mushroom,
|
icon: illustrations.mushroom,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Feature 3',
|
title: 'Feature 3',
|
||||||
description:
|
description:
|
||||||
'Here will say something more about security and how kc is revolutionary when it comes to security',
|
'Here will say something more about security and how kc is revolutionary when it comes to security',
|
||||||
image: illustrations.hand,
|
icon: illustrations.hand,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Mobile friendly',
|
title: 'Mobile friendly',
|
||||||
description:
|
description:
|
||||||
'Keycard is using NFC which is natively embedded in all mobile phones',
|
'Keycard is using NFC which is natively embedded in all mobile phones',
|
||||||
image: illustrations.duck,
|
icon: illustrations.duck,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'No need to charge',
|
title: 'No need to charge',
|
||||||
description: 'Keycard has no battery so it’s always ready to go.',
|
description: 'Keycard has no battery so it’s always ready to go.',
|
||||||
image: illustrations.flower,
|
icon: illustrations.flower,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Easy to carry around',
|
title: 'Easy to carry around',
|
||||||
description:
|
description:
|
||||||
'Its credit card form factor is small and convenient to fit in any wallet.',
|
'Its credit card form factor is small and convenient to fit in any wallet.',
|
||||||
image: illustrations.megaphone,
|
icon: illustrations.megaphone,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
.container {
|
.container {
|
||||||
@apply mx-auto max-w-[1504px] px-5 lg:px-40;
|
@apply mx-auto max-w-[1504px] px-5 lg:px-40;
|
||||||
}
|
}
|
||||||
|
.border-dashed-default {
|
||||||
|
@apply border-dashed border-neutral-80/20;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes gradient {
|
@keyframes gradient {
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
import { cloneElement, forwardRef } from 'react'
|
import { cloneElement, forwardRef } from 'react'
|
||||||
|
|
||||||
import { styled } from '@tamagui/core'
|
import { styled } from '@tamagui/core'
|
||||||
import { Pressable } from 'react-native'
|
import { View } from 'react-native'
|
||||||
|
|
||||||
import { Text } from '../text'
|
import { Text } from '../text'
|
||||||
|
|
||||||
import type { TextProps } from '../text'
|
import type { TextProps } from '../text'
|
||||||
import type { GetVariants, MapVariant, PressableProps } from '../types'
|
import type { GetVariants, MapVariant, PressableProps } from '../types'
|
||||||
import type { Ref } from 'react'
|
import type { Ref } from 'react'
|
||||||
import type { View } from 'react-native'
|
|
||||||
|
|
||||||
type Variants = GetVariants<typeof Base>
|
type Variants = GetVariants<typeof Base>
|
||||||
|
|
||||||
|
@ -94,10 +93,9 @@ const _Button = forwardRef(Button)
|
||||||
export { _Button as Button }
|
export { _Button as Button }
|
||||||
export type { Props as ButtonProps }
|
export type { Props as ButtonProps }
|
||||||
|
|
||||||
const Base = styled(Pressable, {
|
const Base = styled(View, {
|
||||||
tag: 'button',
|
|
||||||
name: 'Button',
|
name: 'Button',
|
||||||
accessibilityRole: 'button',
|
role: 'button',
|
||||||
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { forwardRef } from 'react'
|
||||||
|
|
||||||
import { ArrowDownIcon, MentionIcon } from '@status-im/icons'
|
import { ArrowDownIcon, MentionIcon } from '@status-im/icons'
|
||||||
import { styled } from '@tamagui/core'
|
import { styled } from '@tamagui/core'
|
||||||
import { Pressable } from 'react-native'
|
import { View } from 'react-native'
|
||||||
|
|
||||||
import { Shadow } from '../shadow'
|
import { Shadow } from '../shadow'
|
||||||
import { Text } from '../text'
|
import { Text } from '../text'
|
||||||
|
@ -10,7 +10,6 @@ import { Text } from '../text'
|
||||||
import type { GetVariants, PressableProps } from '../types'
|
import type { GetVariants, PressableProps } from '../types'
|
||||||
import type { ColorTokens } from '@tamagui/core'
|
import type { ColorTokens } from '@tamagui/core'
|
||||||
import type { Ref } from 'react'
|
import type { Ref } from 'react'
|
||||||
import type { View } from 'react-native'
|
|
||||||
|
|
||||||
type Variants = GetVariants<typeof Button>
|
type Variants = GetVariants<typeof Button>
|
||||||
|
|
||||||
|
@ -50,10 +49,9 @@ const _DynamicButton = forwardRef(DynamicButton)
|
||||||
export { _DynamicButton as DynamicButton }
|
export { _DynamicButton as DynamicButton }
|
||||||
export type { Props as DynamicButtonProps }
|
export type { Props as DynamicButtonProps }
|
||||||
|
|
||||||
const Button = styled(Pressable, {
|
const Button = styled(View, {
|
||||||
name: 'DynamicButton',
|
name: 'DynamicButton',
|
||||||
tag: 'button',
|
role: 'button',
|
||||||
accessibilityRole: 'button',
|
|
||||||
|
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
userSelect: 'none',
|
userSelect: 'none',
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
import type { PressableProps } from '../types'
|
import type { PressableProps } from '../types'
|
||||||
import type { MouseEvent } from 'react-native'
|
|
||||||
import type { ColorTokens } from 'tamagui'
|
import type { ColorTokens } from 'tamagui'
|
||||||
|
|
||||||
type Config = {
|
type Config = {
|
||||||
|
@ -49,20 +48,20 @@ export const usePressableColors = (
|
||||||
return {
|
return {
|
||||||
color: styles[key],
|
color: styles[key],
|
||||||
pressableProps: {
|
pressableProps: {
|
||||||
onHoverIn: event => {
|
onHoverIn: (...args) => {
|
||||||
props.onHoverIn?.(event as unknown as MouseEvent)
|
props.onHoverIn?.(...args)
|
||||||
setHovered(true)
|
setHovered(true)
|
||||||
},
|
},
|
||||||
onHoverOut: event => {
|
onHoverOut: (...args) => {
|
||||||
props.onHoverOut?.(event as unknown as MouseEvent)
|
props.onHoverOut?.(...args)
|
||||||
setHovered(false)
|
setHovered(false)
|
||||||
},
|
},
|
||||||
onPressIn: event => {
|
onPressIn: (...args) => {
|
||||||
props.onPressIn?.(event)
|
props.onPressIn?.(...args)
|
||||||
setPressed(true)
|
setPressed(true)
|
||||||
},
|
},
|
||||||
onPressOut: event => {
|
onPressOut: (...args) => {
|
||||||
props.onPressOut?.(event)
|
props.onPressOut?.(...args)
|
||||||
setPressed(false)
|
setPressed(false)
|
||||||
},
|
},
|
||||||
} as const,
|
} as const,
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import { cloneElement, forwardRef } from 'react'
|
import { cloneElement, forwardRef } from 'react'
|
||||||
|
|
||||||
import { Pressable } from 'react-native'
|
import { View } from 'react-native'
|
||||||
import { styled } from 'tamagui'
|
import { styled } from 'tamagui'
|
||||||
|
|
||||||
import { usePressableColors } from '../hooks/use-pressable-colors'
|
import { usePressableColors } from '../hooks/use-pressable-colors'
|
||||||
|
|
||||||
import type { GetVariants, PressableProps } from '../types'
|
import type { GetVariants, PressableProps } from '../types'
|
||||||
import type { Ref } from 'react'
|
import type { Ref } from 'react'
|
||||||
import type { View } from 'react-native'
|
|
||||||
|
|
||||||
type Variants = GetVariants<typeof Base>
|
type Variants = GetVariants<typeof Base>
|
||||||
|
|
||||||
|
@ -63,10 +62,9 @@ const _IconButton = forwardRef(IconButton)
|
||||||
export { _IconButton as IconButton }
|
export { _IconButton as IconButton }
|
||||||
export type { Props as IconButtonProps }
|
export type { Props as IconButtonProps }
|
||||||
|
|
||||||
const Base = styled(Pressable, {
|
const Base = styled(View, {
|
||||||
name: 'IconButton',
|
name: 'IconButton',
|
||||||
tag: 'button',
|
role: 'button',
|
||||||
accessibilityRole: 'button',
|
|
||||||
|
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
userSelect: 'none',
|
userSelect: 'none',
|
||||||
|
|
|
@ -106,7 +106,6 @@ const _Input = (props: Props, ref: Ref<TextInput>) => {
|
||||||
{Boolean(onClear) && !!value && (
|
{Boolean(onClear) && !!value && (
|
||||||
<Stack
|
<Stack
|
||||||
role="button"
|
role="button"
|
||||||
accessibilityRole="button"
|
|
||||||
pr={4}
|
pr={4}
|
||||||
onPress={onClear}
|
onPress={onClear}
|
||||||
cursor="pointer"
|
cursor="pointer"
|
||||||
|
|
|
@ -10,14 +10,13 @@ import {
|
||||||
ThumbsUpIcon,
|
ThumbsUpIcon,
|
||||||
} from '@status-im/icons'
|
} from '@status-im/icons'
|
||||||
import { styled } from '@tamagui/core'
|
import { styled } from '@tamagui/core'
|
||||||
import { Pressable } from 'react-native'
|
import { View } from 'react-native'
|
||||||
|
|
||||||
import { Text } from '../text'
|
import { Text } from '../text'
|
||||||
|
|
||||||
import type { ReactionType } from '../messages/types'
|
import type { ReactionType } from '../messages/types'
|
||||||
import type { PressableProps } from '../types'
|
import type { PressableProps } from '../types'
|
||||||
import type { Ref } from 'react'
|
import type { Ref } from 'react'
|
||||||
import type { View } from 'react-native'
|
|
||||||
|
|
||||||
export const REACTIONS_ICONS = {
|
export const REACTIONS_ICONS = {
|
||||||
love: LoveIcon,
|
love: LoveIcon,
|
||||||
|
@ -63,10 +62,9 @@ const _ReactButton = forwardRef(ReactButton)
|
||||||
export { _ReactButton as ReactButton }
|
export { _ReactButton as ReactButton }
|
||||||
export type { Props as ReactButtonProps }
|
export type { Props as ReactButtonProps }
|
||||||
|
|
||||||
const Button = styled(Pressable, {
|
const Button = styled(View, {
|
||||||
name: 'ReactButton',
|
name: 'ReactButton',
|
||||||
tag: 'button',
|
role: 'button',
|
||||||
accessibilityRole: 'button',
|
|
||||||
|
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
userSelect: 'none',
|
userSelect: 'none',
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Children, cloneElement, forwardRef } from 'react'
|
||||||
|
|
||||||
import { Content, List, Root, Trigger } from '@radix-ui/react-tabs'
|
import { Content, List, Root, Trigger } from '@radix-ui/react-tabs'
|
||||||
import { Stack } from '@tamagui/web'
|
import { Stack } from '@tamagui/web'
|
||||||
import { Pressable } from 'react-native'
|
import { View } from 'react-native'
|
||||||
import { styled } from 'tamagui'
|
import { styled } from 'tamagui'
|
||||||
|
|
||||||
import { Counter } from '../counter'
|
import { Counter } from '../counter'
|
||||||
|
@ -12,7 +12,6 @@ import { Text } from '../text'
|
||||||
import type { TextProps } from '../text'
|
import type { TextProps } from '../text'
|
||||||
import type { GetVariants } from '../types'
|
import type { GetVariants } from '../types'
|
||||||
import type { Ref } from 'react'
|
import type { Ref } from 'react'
|
||||||
import type { View } from 'react-native'
|
|
||||||
|
|
||||||
type Variants = GetVariants<typeof TriggerBase>
|
type Variants = GetVariants<typeof TriggerBase>
|
||||||
|
|
||||||
|
@ -117,8 +116,9 @@ Tabs.Content = Content
|
||||||
export { Tabs }
|
export { Tabs }
|
||||||
export type { Props as TabsProps }
|
export type { Props as TabsProps }
|
||||||
|
|
||||||
const TriggerBase = styled(Pressable, {
|
const TriggerBase = styled(View, {
|
||||||
tag: 'button',
|
name: 'Trigger',
|
||||||
|
role: 'button',
|
||||||
|
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { createElement } from 'react'
|
import { createElement } from 'react'
|
||||||
|
|
||||||
import { Stack, styled } from '@tamagui/core'
|
import { styled } from '@tamagui/core'
|
||||||
|
import { View } from 'react-native'
|
||||||
|
|
||||||
import { Text } from '../text'
|
import { Text } from '../text'
|
||||||
import { getCustomStyles } from './get-custom-styles'
|
import { getCustomStyles } from './get-custom-styles'
|
||||||
|
@ -9,6 +10,7 @@ import type { TextProps } from '../text'
|
||||||
import type { IconProps } from '@status-im/icons'
|
import type { IconProps } from '@status-im/icons'
|
||||||
import type { ColorTokens } from '@tamagui/core'
|
import type { ColorTokens } from '@tamagui/core'
|
||||||
import type { ComponentType } from 'react'
|
import type { ComponentType } from 'react'
|
||||||
|
import type { PressableProps } from 'react-native'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
size: 32 | 24
|
size: 32 | 24
|
||||||
|
@ -16,7 +18,7 @@ type Props = {
|
||||||
label?: string
|
label?: string
|
||||||
selected?: boolean
|
selected?: boolean
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
onPress?: () => void
|
onPress?: PressableProps['onPress']
|
||||||
color?: ColorTokens | `#${string}`
|
color?: ColorTokens | `#${string}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,8 +56,11 @@ const Tag = (props: Props) => {
|
||||||
selected={selected}
|
selected={selected}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
iconOnly={Boolean(icon && !label)}
|
iconOnly={Boolean(icon && !label)}
|
||||||
onPress={() => onPress?.()}
|
|
||||||
{...getCustomStyles(props)}
|
{...getCustomStyles(props)}
|
||||||
|
{...(onPress && {
|
||||||
|
role: 'button',
|
||||||
|
onPress,
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
{renderIcon()}
|
{renderIcon()}
|
||||||
{label && (
|
{label && (
|
||||||
|
@ -70,11 +75,10 @@ const Tag = (props: Props) => {
|
||||||
export { Tag }
|
export { Tag }
|
||||||
export type { Props as TagProps }
|
export type { Props as TagProps }
|
||||||
|
|
||||||
const Base = styled(Stack, {
|
const Base = styled(View, {
|
||||||
tag: 'tag',
|
|
||||||
name: 'Tag',
|
name: 'Tag',
|
||||||
accessibilityRole: 'button',
|
|
||||||
|
|
||||||
|
userSelect: 'none',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
@ -85,7 +89,6 @@ const Base = styled(Stack, {
|
||||||
backgroundColor: '$white-100',
|
backgroundColor: '$white-100',
|
||||||
|
|
||||||
animation: 'fast',
|
animation: 'fast',
|
||||||
cursor: 'pointer',
|
|
||||||
|
|
||||||
hoverStyle: {
|
hoverStyle: {
|
||||||
borderColor: '$neutral-30',
|
borderColor: '$neutral-30',
|
||||||
|
|
|
@ -8,12 +8,12 @@ import type {
|
||||||
import type { PressableProps as NativePressableProps } from 'react-native'
|
import type { PressableProps as NativePressableProps } from 'react-native'
|
||||||
|
|
||||||
type PressableProps = {
|
type PressableProps = {
|
||||||
onHoverIn?: Exclude<NativePressableProps['onHoverIn'], null>
|
onHoverIn?: VoidFunction
|
||||||
onHoverOut?: NativePressableProps['onHoverOut']
|
onHoverOut?: VoidFunction
|
||||||
onPress?: NativePressableProps['onPress']
|
onPress?: VoidFunction
|
||||||
onPressIn?: NativePressableProps['onPressIn']
|
onPressIn?: VoidFunction
|
||||||
onPressOut?: NativePressableProps['onPressOut']
|
onPressOut?: VoidFunction
|
||||||
onLongPress?: NativePressableProps['onLongPress']
|
onLongPress?: VoidFunction
|
||||||
delayHoverIn?: NativePressableProps['delayHoverIn']
|
delayHoverIn?: NativePressableProps['delayHoverIn']
|
||||||
delayHoverOut?: NativePressableProps['delayHoverOut']
|
delayHoverOut?: NativePressableProps['delayHoverOut']
|
||||||
delayLongPress?: NativePressableProps['delayLongPress']
|
delayLongPress?: NativePressableProps['delayLongPress']
|
||||||
|
|
Loading…
Reference in New Issue