mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
Inline style objects became gui props (Box for a div, View or Text rendering the element for the rest, since Box styles only a div), raw colours and sizes became the design's tokens, the brand swatches read @hanzo/design's own values, and the project and creation hues moved to palette.json, which is data. The newsletter's subscribe button is the standard filled Action. connect-steps.tsx, which nothing imports, is deleted with the brand constants nobody read. Pages compared before and after at 1440x900 and 390x844: blockchain, brand, directory, projects, settings, unsubscribe identical; founder 0.03% of pixels; buying specialist's title snaps from 28 to the ramp's 26px and the newsletter's button is now filled. Claude-Session: https://claude.ai/code/session_017vrVus6PPBgP5YRzKV1iqW
128 lines
3.9 KiB
TypeScript
128 lines
3.9 KiB
TypeScript
'use client'
|
|
|
|
import React, { useState } from 'react'
|
|
import { Mail, Check, ArrowRight } from 'lucide-react'
|
|
import { Button, Input, Text, toast, View, XStack, YStack } from '@hanzo/ui'
|
|
|
|
const api = () => 'https://api.hanzo.ai'
|
|
const track = (_event: string, _props?: Record<string, any>) => {}
|
|
const visit = () => ({})
|
|
|
|
interface NewsletterSignupProps {
|
|
title?: string
|
|
description?: string
|
|
source?: string
|
|
}
|
|
|
|
export function NewsletterSignup({
|
|
title = 'Stay on the frontier',
|
|
description = 'Weekly updates on new model releases, benchmarks, and agent architectures.',
|
|
source = 'hanzo.ai/newsletter',
|
|
}: NewsletterSignupProps) {
|
|
const [email, setEmail] = useState('')
|
|
const [busy, setBusy] = useState(false)
|
|
const [subscribed, setSubscribed] = useState(false)
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
const trimmed = email.trim()
|
|
if (!trimmed || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmed)) {
|
|
toast.error('Please enter a valid email address')
|
|
return
|
|
}
|
|
|
|
setBusy(true)
|
|
track('newsletter_submitting', { source })
|
|
|
|
try {
|
|
const res = await fetch(`${api()}/v1/marketing/leads`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
email: trimmed,
|
|
source,
|
|
consent: true,
|
|
program: 'newsletter',
|
|
stage: 'subscriber',
|
|
visit: visit(),
|
|
}),
|
|
})
|
|
|
|
if (res.ok) {
|
|
setSubscribed(true)
|
|
track('newsletter_subscribed', { source, email: trimmed })
|
|
toast.success('Subscribed! Welcome to the Hanzo developer dispatch.')
|
|
} else {
|
|
toast.error('Unable to subscribe right now. Please try again.')
|
|
}
|
|
} catch {
|
|
toast.error('Network error. Check your connection.')
|
|
} finally {
|
|
setBusy(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<YStack gap={12} width="100%" maxW={460}>
|
|
{title && (
|
|
<Text fontSize="$4" fontWeight="600" color="var(--foreground)">
|
|
{title}
|
|
</Text>
|
|
)}
|
|
{description && (
|
|
<Text fontSize="$2" color="var(--muted-foreground)">
|
|
{description}
|
|
</Text>
|
|
)}
|
|
|
|
{subscribed ? (
|
|
<XStack items="center" gap={8} py={8}>
|
|
<Check size={16} color="var(--state-success)" />
|
|
<Text fontSize="$2" color="var(--state-success)" fontWeight="500">
|
|
You're subscribed to the Hanzo newsletter.
|
|
</Text>
|
|
</XStack>
|
|
) : (
|
|
<View render={<form onSubmit={handleSubmit} />} display="block" width="100%">
|
|
<XStack gap={8} width="100%">
|
|
<Input
|
|
type="email"
|
|
placeholder="Enter your email"
|
|
value={email}
|
|
onChange={(e: any) => setEmail(e.target.value)}
|
|
height={42}
|
|
rounded={10}
|
|
bg="var(--surface-1)"
|
|
borderColor="var(--border)"
|
|
flex={1}
|
|
/>
|
|
<Button
|
|
type="submit"
|
|
height={42}
|
|
px={16}
|
|
rounded={10}
|
|
bg="var(--foreground)"
|
|
color="var(--background)"
|
|
disabled={busy}
|
|
gap={6}
|
|
>
|
|
{busy ? '...' : 'Subscribe'}
|
|
{!busy && <ArrowRight size={14} />}
|
|
</Button>
|
|
</XStack>
|
|
<Text fontSize="$1" color="var(--muted-foreground)" mt={8} lineHeight={14}>
|
|
By subscribing, you agree to receive product updates and engineering dispatches from Hanzo AI Inc. (995 Market St, SF CA). You can unsubscribe anytime via the 1-click link in every email. View our{' '}
|
|
<Text
|
|
render={<a href="/legal/privacy" />}
|
|
color="var(--foreground)"
|
|
textDecorationLine="underline"
|
|
>Privacy Policy</Text>.
|
|
</Text>
|
|
</View>
|
|
)}
|
|
</YStack>
|
|
)
|
|
}
|
|
|
|
export default NewsletterSignup
|