Files
zeekay 6c65e3e941 @hanzo/app 0.2.0: the app SDK, renamed from @hanzo/rooms
Every import, the catalog pin and the docs name @hanzo/app and
github.com/hanzoai/app. lib/rooms.ts is lib/app.ts and
components/rooms.tsx is components/app.tsx.
2026-10-22 19:07:20 -07:00

482 lines
19 KiB
TypeScript

"use client"
import { useState, type FormEvent } from "react"
import { motion } from "@/components/motion"
import {
Briefcase,
Globe,
Clock,
MapPin,
Users,
Sparkles,
ArrowRight,
ArrowLeft,
CheckCircle2,
Copy,
Check,
} from "lucide-react"
import { Box, Button, Input, Text, Textarea, View, XStack, YStack } from '@hanzo/ui'
import { Band } from '@/components/marketing/band'
import { Action, Chip, Display, Lede, Title, Line } from '@hanzo/ui/marketing'
import { api } from '@hanzo/app/lib/api'
import { track } from '@hanzo/app/lib/tags'
import type { JobOpening } from '@/lib/careers'
import { ResumeAndConsent, resumeProblem, uploadResume } from '../apply'
const INK = 'var(--foreground)'
const DIM = 'var(--muted-foreground)'
const SECTION_BOX = {
p: 28,
rounded: 'var(--radius-xl)',
borderWidth: 1,
borderColor: 'var(--border)',
bg: 'var(--secondary)',
} as const
/** An address as https: one typed without a scheme gets https://, and cloud drops any other. */
const https = (u: string): string => {
const t = u.trim()
return !t || /^[a-z][a-z0-9+.-]*:/i.test(t) ? t : `https://${t}`
}
export function JobView({ job }: { job: JobOpening }) {
const [form, setForm] = useState({
name: '',
email: '',
github: '',
linkedin: '',
location: '',
resume: '',
notes: '',
})
const [file, setFile] = useState<File | null>(null)
const [consent, setConsent] = useState(false)
const [busy, setBusy] = useState(false)
const [sent, setSent] = useState(false)
const [wrong, setWrong] = useState<string | null>(null)
const [copied, setCopied] = useState(false)
const copyLink = () => {
if (typeof window !== 'undefined') {
navigator.clipboard.writeText(window.location.href)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
}
}
const handleSubmit = async (e: FormEvent) => {
e.preventDefault()
if (busy) return
if (!form.name.trim() || !form.email.trim() || !form.notes.trim()) {
setWrong('Please fill in your name, email, and background note.')
return
}
const EMAIL_TEST = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!EMAIL_TEST.test(form.email.trim())) {
setWrong('Please enter a valid email address.')
return
}
if (file && resumeProblem(file)) {
setWrong(resumeProblem(file))
return
}
setWrong(null)
setBusy(true)
let resumeFile = ''
try {
if (file) resumeFile = await uploadResume(file)
} catch {
setWrong('Your resume did not upload. Please try again, or give a link instead.')
setBusy(false)
return
}
const payload = {
name: form.name.trim(),
email: form.email.trim(),
role: job.title,
department: job.department,
country: form.location.trim() || job.location,
source: `hanzo.ai/careers/${job.id}`,
// An application to an opening is a candidate in the CRM (cloud apps/marketing
// leads.go `job`): what the person wrote, their resume and their links, with no
// call offered and nothing sent to the ad platforms.
job: job.id,
need: form.notes.trim(),
resume: https(form.resume),
links: [https(form.github), https(form.linkedin)].filter(Boolean),
resume_file: resumeFile,
consent,
}
try {
const r = await fetch(`${api()}/v1/marketing/leads`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
if (!r.ok) throw new Error(`HTTP ${r.status}`)
track('career_application_submitted', { role: payload.role, department: payload.department, slug: job.id })
setSent(true)
} catch (err) {
track('career_application_submitted', { role: payload.role, department: payload.department, slug: job.id, error: String(err) })
setWrong('Your application did not reach us. Please try again, or email it to [email protected].')
} finally {
setBusy(false)
}
}
return (
<Box minH="100vh" $platform-web={{ color: INK }}>
<main>
{/* Top Header / Breadcrumb */}
<Box
render="section"
position="relative"
overflow="hidden"
pt={80}
pb={48}
px={16}
$md={{ px: 32 }}
$lg={{ px: 48 }}
>
<View
aria-hidden
position="absolute"
t={0}
l="50%"
x="-50%"
width={800}
height={360}
backgroundImage="radial-gradient(circle, color-mix(in srgb, var(--primary) 12%, transparent) 0%, transparent 70%)"
filter="blur(80px)"
pointerEvents="none"
/>
<Box position="relative" z="var(--z-raised)" width="100%" maxW={1024} mx="auto">
<XStack items="center" justify="space-between" mb={24} flexWrap="wrap" gap={12}>
<Line href="/careers" display="inline-flex" items="center" gap={6} fontSize="$3" color={DIM}>
<ArrowLeft width={16} height={16} />
Back to all careers
</Line>
<Button variant="ghost" size="sm" onPress={copyLink}>
{copied ? <Check size={14} color="var(--primary)" /> : <Copy size={14} />}
{copied ? 'Link copied' : 'Share position'}
</Button>
</XStack>
<XStack gap={8} mb={16} flexWrap="wrap" items="center">
<Chip fontSize="$1" lineHeight="1.3333" fontWeight="500" borderColor="var(--border)" bg="color-mix(in oklab, var(--pure-white) 15%, transparent)" color={INK}>
<Briefcase width={13} height={13} />
{job.department}
</Chip>
<Chip fontSize="$1" lineHeight="1.3333" fontWeight="500" borderColor="var(--border)" bg="transparent" color={DIM}>
<MapPin width={13} height={13} />
{job.location}
</Chip>
<Chip fontSize="$1" lineHeight="1.3333" fontWeight="500" borderColor="var(--border)" bg="transparent" color={DIM}>
<Clock width={13} height={13} />
{job.type}
</Chip>
</XStack>
<Display render={<motion.h1 initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.4 }} />} mb={16}>
{job.title}
</Display>
<Lede render={<motion.p initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.4, delay: 0.05 }} />} mb={32} width="100%" maxW={768}>
{job.description}
</Lede>
<XStack gap={16} flexWrap="wrap" items="center">
<Action href="#apply" fill>
Apply for this position
<ArrowRight width={16} height={16} />
</Action>
<Action href={`mailto:[email protected]?subject=${encodeURIComponent(`Application: ${job.title}`)}`}>
Email resume directly
</Action>
</XStack>
</Box>
</Box>
{/* Content Body Grid */}
<Band rule={false} pad="$10" measure={1024}>
<Box display="grid" gap={32} gridTemplateColumns="minmax(0, 1fr)" $lg={{ gridTemplateColumns: 'minmax(0, 1.4fr) minmax(0, 1fr)', items: 'flex-start' }}>
{/* Left: Job Description & Details */}
<YStack gap={32}>
<Box {...SECTION_BOX}>
<Title quiet mb={16} fontSize="$5" fontWeight="600">
About the Role
</Title>
<Line render="p" display="block" fontSize="$3" lineHeight="1.6" color={INK} mb={16}>
{job.about}
</Line>
<Line render="p" display="block" fontSize="$2" lineHeight="1.5" color={DIM}>
Hanzo builds an open AI cloud — compilers, models, distributed infrastructure, and agent tooling. Our codebases are predominantly open source, and team members operate with high autonomy and end-to-end ownership.
</Line>
</Box>
<Box {...SECTION_BOX}>
<Title quiet mb={16} fontSize="$5" fontWeight="600">
Key Responsibilities
</Title>
<YStack gap={12}>
{job.responsibilities.map((r, i) => (
<XStack key={i} items="flex-start" gap={10}>
<View
width={6}
height={6}
rounded="var(--radius-full)"
bg="var(--primary)"
mt={8}
shrink={0}
/>
<Line render="p" fontSize="$2" lineHeight="1.5" color={INK}>
{r}
</Line>
</XStack>
))}
</YStack>
</Box>
<Box {...SECTION_BOX}>
<Title quiet mb={16} fontSize="$5" fontWeight="600">
What We&apos;re Looking For
</Title>
<YStack gap={12}>
{job.requirements.map((req, i) => (
<XStack key={i} items="flex-start" gap={10}>
<View
width={6}
height={6}
rounded="var(--radius-full)"
bg="var(--primary)"
mt={8}
shrink={0}
/>
<Line render="p" fontSize="$2" lineHeight="1.5" color={INK}>
{req}
</Line>
</XStack>
))}
</YStack>
{job.niceToHave && job.niceToHave.length > 0 ? (
<>
<Title quiet mt={24} mb={12} fontSize="$4" fontWeight="600">
Nice to Have
</Title>
<YStack gap={12}>
{job.niceToHave.map((nth, i) => (
<XStack key={i} items="flex-start" gap={10}>
<View
width={6}
height={6}
rounded="var(--radius-full)"
bg="var(--border)"
mt={8}
shrink={0}
/>
<Line render="p" fontSize="$2" lineHeight="1.5" color={DIM}>
{nth}
</Line>
</XStack>
))}
</YStack>
</>
) : null}
</Box>
<Box {...SECTION_BOX}>
<Title quiet mb={16} fontSize="$5" fontWeight="600">
Why Join Hanzo
</Title>
<YStack gap={12}>
<XStack items="flex-start" gap={12}>
<View render={<Globe size={18} color="var(--primary)" />} mt={2} shrink={0} />
<YStack flex={1} minW={0}>
<Line fontWeight="600" fontSize="$2" color={INK}>Remote-first culture</Line>
<Line fontSize="$2" color={DIM}>Distributed globally. We value outcomes over hours spent at a desk.</Line>
</YStack>
</XStack>
<XStack items="flex-start" gap={12}>
<View render={<Sparkles size={18} color="var(--primary)" />} mt={2} shrink={0} />
<YStack flex={1} minW={0}>
<Line fontWeight="600" fontSize="$2" color={INK}>Public, open-source output</Line>
<Line fontSize="$2" color={DIM}>Ship work that developers worldwide inspect, clone, and build on.</Line>
</YStack>
</XStack>
<XStack items="flex-start" gap={12}>
<View render={<Users size={18} color="var(--primary)" />} mt={2} shrink={0} />
<YStack flex={1} minW={0}>
<Line fontWeight="600" fontSize="$2" color={INK}>High agency & ownership</Line>
<Line fontSize="$2" color={DIM}>Direct access to leadership and the autonomy to build whole systems.</Line>
</YStack>
</XStack>
</YStack>
</Box>
</YStack>
{/* Right: Application Form */}
<Box id="apply" {...SECTION_BOX} position="sticky" t={96}>
<Title quiet mb={8} fontSize="$5" fontWeight="700">
Apply for this Role
</Title>
<Text fontSize="$2" color={DIM} mb={20} display="block">
Submit your information below to be directly reviewed by our team.
</Text>
{sent ? (
<YStack gap="$4" py="$4" items="center">
<CheckCircle2 size={48} color="var(--primary)" />
<YStack gap="$2" items="center">
<Text fontSize="$4" fontWeight="600" color={INK}>
Application Received!
</Text>
<Text fontSize="$2" color={DIM} $platform-web={{ textAlign: 'center' }}>
Thank you for applying for <Text fontWeight="600" color={INK}>{job.title}</Text>. Our team reviews submissions promptly and will contact you at <Text fontWeight="600" color={INK}>{form.email}</Text>.
</Text>
<Text fontSize="$1" color={DIM} mt="$2" $platform-web={{ textAlign: 'center' }}>
You can also email your resume or portfolio directly to{' '}
<Line
href={`mailto:[email protected]?subject=${encodeURIComponent(`Application: ${job.title} - ${form.name}`)}`}
color="var(--primary)"
textDecorationLine="underline"
>
careers@hanzo.ai
</Line>.
</Text>
</YStack>
<Button
variant="primary"
onPress={() => setSent(false)}
mt="$2"
>
Submit Another Application
</Button>
</YStack>
) : (
<YStack render="form" {...{ onSubmit: handleSubmit }} gap={14}>
<YStack gap="$1">
<Text fontSize="$2" fontWeight="500" color={INK}>
Full Name <Text render="span" color="var(--destructive)">*</Text>
</Text>
<Input
value={form.name}
onChange={(e) => setForm((f) => ({ ...f, name: e.target.value }))}
placeholder="Ada Lovelace"
required
/>
</YStack>
<YStack gap="$1">
<Text fontSize="$2" fontWeight="500" color={INK}>
Email Address <Text render="span" color="var(--destructive)">*</Text>
</Text>
<Input
type="email"
value={form.email}
onChange={(e) => setForm((f) => ({ ...f, email: e.target.value }))}
placeholder="[email protected]"
required
/>
</YStack>
<YStack gap="$1">
<Text fontSize="$2" fontWeight="500" color={INK}>
GitHub / Portfolio URL
</Text>
<Input
value={form.github}
onChange={(e) => setForm((f) => ({ ...f, github: e.target.value }))}
placeholder="https://github.com/adalovelace"
/>
</YStack>
<YStack gap="$1">
<Text fontSize="$2" fontWeight="500" color={INK}>
LinkedIn / Personal Site
</Text>
<Input
value={form.linkedin}
onChange={(e) => setForm((f) => ({ ...f, linkedin: e.target.value }))}
placeholder="https://linkedin.com/in/ada"
/>
</YStack>
<YStack gap="$1">
<Text fontSize="$2" fontWeight="500" color={INK}>
Resume link
</Text>
<Input
value={form.resume}
onChange={(e) => setForm((f) => ({ ...f, resume: e.target.value }))}
placeholder="A link to your resume: PDF, Google Drive or Dropbox"
/>
</YStack>
<ResumeAndConsent file={file} onFile={setFile} consent={consent} onConsent={setConsent} />
<YStack gap="$1">
<Text fontSize="$2" fontWeight="500" color={INK}>
Location / Timezone
</Text>
<Input
value={form.location}
onChange={(e) => setForm((f) => ({ ...f, location: e.target.value }))}
placeholder="e.g. San Francisco, CA or Remote (UTC-8)"
/>
</YStack>
<YStack gap="$1">
<Text fontSize="$2" fontWeight="500" color={INK}>
Background & Notes <Text render="span" color="var(--destructive)">*</Text>
</Text>
<Textarea
value={form.notes}
onChange={(e) => setForm((f) => ({ ...f, notes: e.target.value }))}
placeholder="Tell us what systems you've built, relevant experience, or why you're interested in this role."
rows={5}
required
/>
</YStack>
{wrong ? (
<Text fontSize="$2" color="var(--destructive)">
{wrong}
</Text>
) : null}
<Button
variant="primary"
size="lg"
type="submit"
disabled={busy}
width="100%"
mt="$2"
>
{busy ? 'Submitting Application…' : 'Submit Application'}
</Button>
<Text fontSize="$1" color={DIM} $platform-web={{ textAlign: 'center' }} mt="$1">
Prefer email? Send your resume to{' '}
<Line
href={`mailto:[email protected]?subject=${encodeURIComponent(`Application: ${job.title}`)}`}
color="var(--primary)"
textDecorationLine="underline"
>
careers@hanzo.ai
</Line>
</Text>
</YStack>
)}
</Box>
</Box>
</Band>
</main>
</Box>
)
}