mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-30 14:58:08 +00:00
The product pages from /s3 to /zap, /university and its course data, /trust, /security, /sdks, /support, /tasks, /startups, /sms-opt-in, the buying specialist, Zen, Kai's guide, Studio, the settings pages, and the shared sales, solutions, zap and ui pieces. Client code draws with t(), server components with <T>, copy held as data is marked with phrase() and drawn through t() where it lands. A link or a figure inside a sentence is a value, so a translator reorders the whole sentence. Rebased on main: every page main moved onto Fold or Steps keeps main's layout, with its words wrapped (label, title, lede, points). Steps now draws a step's body through t, as it already drew the title. Settings keeps main's usage panel, refresh, rewards wallet and sign-in wallet rows, each label wrapped. Left English: the skills page's install line (a command and two URLs), and the solution eyebrow and sibling heading built from a kind label. Claude-Session: https://claude.ai/code/session_015XYKLjZtDTJEnMo4eK3AFH
173 lines
6.5 KiB
TypeScript
173 lines
6.5 KiB
TypeScript
/**
|
||
* /support — the Help Center, the docs, live status, and a way to reach a human.
|
||
*
|
||
* NO TAILWIND except one prose-link hover, named at its call site. This is a
|
||
* SERVER page and stays one: no `Head` here, because `Head` carries a scroll
|
||
* entrance and this page has never had motion — giving it one would be a change
|
||
* to the picture smuggled in as a conversion. The frame is `Box render="section"`
|
||
* with the paddings the page already stated; the type and the controls are the
|
||
* @hanzo/ui's `Display`, `Lede`, `Chip`, `Leaf` and `More`.
|
||
*
|
||
* Each channel card is a `Leaf`, the site's card-that-IS-a-link, and its call
|
||
* to action lifts with it: the card decides the hover, the content says who
|
||
* takes part.
|
||
*/
|
||
|
||
import type { Metadata } from 'next'
|
||
import { LifeBuoy, BookOpen, Activity, Mail, MessagesSquare, ArrowUpRight } from 'lucide-react'
|
||
import { Github } from '@/components/brand'
|
||
import { Box, Text } from '@/components/ui'
|
||
import { mix } from '@hanzo/app/lib/mix'
|
||
import { More, Leaf } from '@hanzo/ui/marketing'
|
||
import { Fold } from '@/components/marketing/fold'
|
||
import { Grid } from '@hanzo/ui/grid'
|
||
import { phrase } from '@hanzo/i18n'
|
||
import { T } from '@hanzo/i18n/react'
|
||
|
||
export const metadata: Metadata = {
|
||
title: 'Support — Hanzo',
|
||
description: 'Get help with Hanzo — the Help Center, documentation, live system status, and a way to reach a human.',
|
||
}
|
||
|
||
const CHANNELS = [
|
||
{
|
||
icon: LifeBuoy,
|
||
title: phrase('Help Center'),
|
||
body: phrase('Search answers, browse guides, and open a support ticket. The fastest path to a resolution.'),
|
||
href: 'https://hanzo.help',
|
||
cta: phrase('Open Help Center'),
|
||
},
|
||
{
|
||
icon: BookOpen,
|
||
title: phrase('Documentation'),
|
||
body: phrase('Guides, API references, and quickstarts for every product across the Hanzo Cloud.'),
|
||
href: 'https://docs.hanzo.ai',
|
||
cta: phrase('Read the docs'),
|
||
},
|
||
{
|
||
icon: Activity,
|
||
title: phrase('System Status'),
|
||
body: phrase('Live uptime and incident history for every Hanzo service. Check here first if something feels off.'),
|
||
href: 'https://status.hanzo.ai',
|
||
cta: phrase('View status'),
|
||
},
|
||
{
|
||
icon: MessagesSquare,
|
||
title: phrase('Community'),
|
||
body: phrase('Ask questions and share what you build with other developers on the Hanzo Discord and Community.'),
|
||
href: 'https://discord.gg/XthHQQj',
|
||
cta: phrase('Join the community'),
|
||
},
|
||
{
|
||
icon: Github,
|
||
title: 'GitHub',
|
||
body: phrase('Report a bug, request a feature, or contribute — our SDKs and tooling are open source.'),
|
||
href: 'https://github.com/hanzoai',
|
||
cta: phrase('Open an issue'),
|
||
},
|
||
{
|
||
icon: Mail,
|
||
title: phrase('Email us'),
|
||
body: phrase('Prefer email? Reach the team directly and we’ll get back to you.'),
|
||
href: 'mailto:[email protected]',
|
||
cta: '[email protected]',
|
||
},
|
||
]
|
||
|
||
/** The hairline tile this page sets a card and a footnote in. */
|
||
const TILE = {
|
||
borderRadius: 'var(--radius-xl)',
|
||
borderWidth: 1,
|
||
borderColor: 'var(--neutral-800)',
|
||
backgroundColor: 'color-mix(in oklab, var(--neutral-900) 50%, transparent)',
|
||
padding: 24,
|
||
} as const
|
||
|
||
/** A card: the tile above, plus the edge lift `Leaf` owns. */
|
||
const CARD = { p: 24, borderColor: "var(--neutral-800)", display: "flex", flexDirection: "column", bg: "color-mix(in oklab, var(--neutral-900) 50%, transparent)", hoverStyle: { borderColor: "var(--neutral-600)" } } as const
|
||
|
||
/** The disc a channel's mark sits on; the mark paints in its ink. */
|
||
const MARK = {
|
||
render: 'span',
|
||
mb: 16,
|
||
display: 'inline-flex',
|
||
height: 40,
|
||
width: 40,
|
||
items: 'center',
|
||
justify: 'center',
|
||
rounded: 'var(--radius-lg)',
|
||
borderWidth: 1,
|
||
borderColor: 'var(--neutral-800)',
|
||
bg: mix('var(--pure-white)', 5),
|
||
color: 'var(--foreground)',
|
||
} as const
|
||
|
||
export default function SupportPage() {
|
||
return (
|
||
<Box render="main" position="relative" overflow="hidden">
|
||
{/* The first screen: the question, and every channel that answers it under it. */}
|
||
<Fold
|
||
label={phrase('Support')}
|
||
mark={<LifeBuoy width={12} height={12} color="currentColor" />}
|
||
title={phrase('How can we help?')}
|
||
lede={phrase('Answers, docs, live status, and a direct line to the team.')}
|
||
>
|
||
<Box width="100%" maxW={1152} mx="auto">
|
||
<Grid columns={{ min: 300, max: 3 }} gap={16}>
|
||
{CHANNELS.map((c) => {
|
||
const Icon = c.icon
|
||
const external = c.href.startsWith('http')
|
||
return (
|
||
<Leaf
|
||
render="a"
|
||
key={c.title}
|
||
href={c.href}
|
||
target={external ? '_blank' : undefined}
|
||
rel={external ? 'noopener noreferrer' : undefined}
|
||
{...CARD}
|
||
hoverStyle={{ borderColor: "var(--neutral-600)" }}
|
||
>
|
||
<Text {...MARK}>
|
||
<Icon width={20} height={20} />
|
||
</Text>
|
||
<Text render="h2" mb={6} fontSize="$4" lineHeight="1.2891rem" fontWeight="600" color="var(--foreground)">
|
||
<T>{c.title}</T>
|
||
</Text>
|
||
<Text render="p" mb={20} grow={1} shrink={1} flexBasis="0%" fontSize="$2" lineHeight="1.3203rem" color="var(--muted-foreground)">
|
||
<T>{c.body}</T>
|
||
</Text>
|
||
<More render="span" gap={6} $group-hover={{ color: 'var(--text-primary)' }}>
|
||
<T>{c.cta}</T> <ArrowUpRight width={16} height={16} data-i18n-mirror />
|
||
</More>
|
||
</Leaf>
|
||
)
|
||
})}
|
||
</Grid>
|
||
|
||
{/* Enterprise footer note */}
|
||
<Box mt={24} {...TILE}>
|
||
<Text render="p" display="block" fontSize="$2" text="center" color="var(--muted-foreground)">
|
||
{/* A link in a sentence, not a control: the underline appearing on
|
||
approach is the whole treatment, and @hanzo/design's base
|
||
already states it once for every link inside a paragraph
|
||
(`:is(p,li,…) a:hover{text-decoration:underline}`), so this one
|
||
says nothing. */}
|
||
<T
|
||
values={{
|
||
link: (
|
||
<Text render="a" {...{ href: 'mailto:[email protected]' }} fontWeight="500" color="var(--foreground)">
|
||
<T values={{ email: '[email protected]' }}>{'Email {email}'}</T>
|
||
</Text>
|
||
),
|
||
}}
|
||
>
|
||
{'On an enterprise plan or need a dedicated channel? {link} and we’ll route you to your team.'}
|
||
</T>
|
||
</Text>
|
||
</Box>
|
||
</Box>
|
||
</Fold>
|
||
</Box>
|
||
)
|
||
}
|