Files
hanzo.ai/components/cloud/CloudPrimitiveOverview.tsx
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

281 lines
9.7 KiB
TypeScript

import type React from 'react'
import { Check } from 'lucide-react'
import { Github } from '@/components/brand'
import { AgentPrompt } from '@/components/product/AgentPrompt'
import { Related } from '@/components/product/Related'
import { Surface } from '@/components/product/Surface'
import { type CloudCategory, type Primitive } from '@/lib/data/cloud-primitives'
import { POSITIONING } from '@/lib/layers'
import { product } from '@/lib/product'
import { Box, Text, View, XStack } from '@/components/ui'
import { Action, Display, Lede, Line } from '@hanzo/ui/marketing'
import { tap } from '@hanzo/app/lib/tap'
import { Grid } from '@hanzo/ui/grid'
const STATUS_LABEL: Record<NonNullable<Primitive['status']>, string> = {
ga: 'Generally available',
beta: 'Beta',
}
/**
* Canonical overview page for any cloud primitive that doesn't have a bespoke
* marketing page yet. Data-driven, monochrome, and self-contained so every
* mega-menu leaf resolves to a real, unique page — never a 404, never an empty
* stub. One component renders them all (DRY).
*
* The FACTS are what keep the second half of that promise. The taxonomy is
* hydrated from the commerce catalog now, so a product can reach this page with
* no marketing copy written for it yet — and a page holding a name and two CTAs
* is the empty stub this component exists to avoid. What the catalog does state
* about every product is specific and true: the operation that answers for it,
* and the Google Cloud service a reader may already know it by. Those go above
* the copy, so a page without prose still says what the thing is and where its
* API is, rather than pretending to say more.
*
* The two blocks a written product page ends with are here too — the agent
* prompt under the hero, `Related` last — off the same `lib/product`
* value, so a generated page offers what a written one offers and says the
* command and the docs link the audit verified, or says neither.
*/
/**
* The two strengths of the foreground this page reads in: 80% for what the
* product says, 70% for the category in the crumb. Theme tokens with an
* opacity, so both follow the ink when the theme flips.
*/
const INK_80 = '$ink/80'
const INK_70 = '$ink/70'
/** A word in an outline: the product's status and its two chips. */
const PILL = {
display: 'inline-flex',
items: 'center',
gap: '$2',
borderWidth: 1,
borderColor: '$edge',
rounded: 'var(--radius-full)',
bg: 'var(--glass)',
fontSize: '$1',
lineHeight: '$1',
color: 'var(--text-secondary)',
} as const
/** One fact of a definition list: its term in a fixed column, then the value. */
function Fact({ term, mono, children }: { term: string; mono?: boolean; children: React.ReactNode }) {
return (
<XStack gap="$4">
<Text render="dt" width={96} shrink={0} color="var(--muted-foreground)">
{term}
</Text>
<Text render="dd" m={0} minW={0} fontFamily={mono ? '$mono' : undefined} color={INK_80}>
{children}
</Text>
</XStack>
)
}
export function CloudPrimitiveOverview({
primitive,
category,
}: {
primitive: Primitive
category?: CloudCategory
}) {
const Icon = primitive.icon
// The same value the written product pages hand those two blocks, so a
// generated page says what one of them says, in the same words.
const about = product(primitive.slug)
return (
<Box minH="100vh" $platform-web={{ color: 'var(--foreground)' }}>
<Box
render="section"
width="100%"
maxW={1024}
mx="auto"
px="$5"
pt={112}
pb="$12"
>
{/* Breadcrumb — reads like a cloud console path */}
<Text render="nav" display="flex" mb="$7" items="center" gap="$2" fontSize="$2" lineHeight="1.15rem" color="var(--muted-foreground)">
<Line href="/products" {...tap} color="var(--text-primary)">
Cloud
</Line>
{category && (
<>
<Text aria-hidden color="inherit">
/
</Text>
<Text color={INK_70}>{category.title}</Text>
</>
)}
</Text>
{/* Hero */}
<XStack items="flex-start" gap={20}>
<Text
display="flex"
width={56}
height={56}
shrink={0}
items="center"
justify="center"
rounded="var(--radius-2xl)"
borderWidth={1}
borderColor="$edge"
bg="var(--neutral-900)"
color="$ink"
>
{/* The catalog's icon takes `size` and a class and nothing else, so
its ink comes from `currentColor` on the tile around it. */}
<Icon size={28} />
</Text>
<Box minW={0}>
{primitive.status && (
<Text {...PILL} mb={12} py={2} px={10} fontWeight="500">
{STATUS_LABEL[primitive.status]}
</Text>
)}
<Display>{primitive.title}</Display>
{primitive.tagline && (
<Lede mt={12}>
{primitive.tagline}
</Lede>
)}
</Box>
</XStack>
{/* Open-source + on-chain chips */}
<XStack mt="$5" flexWrap="wrap" items="center" gap="$2">
<Text {...PILL} py={4} px={10}>
Open source
</Text>
<Text {...PILL} py={4} px={10}>
On-chain settlement
</Text>
</XStack>
{/* What the catalog states — the route that answers for this product,
and the Google Cloud service it stands in for. */}
{/* Two facts side by side above ~520px of measure, stacked under it. */}
<Text
render="dl"
display="grid"
gridTemplateColumns="repeat(auto-fit, minmax(min(240px, 100%), 1fr))"
columnGap={40}
rowGap={12}
m={0}
mt={32}
fontSize="$2"
lineHeight="$2"
>
{primitive.api && (
<Fact term="API" mono>
{primitive.api}
</Fact>
)}
{primitive.gcp && <Fact term="Google Cloud">{primitive.gcp}</Fact>}
</Text>
</Box>
{/* The prompt a reader hands to an agent, between what the product IS and
the product running. */}
{about && <AgentPrompt product={about} />}
<Box
render="section"
width="100%"
maxW={1024}
mx="auto"
px="$5"
pt="$12"
pb="$12"
>
{/* The product's own surface, drawn — its corner of the console, the
route it answers on, and one example project running in it. It is
composed from the same catalog this page is routed on, so a product
added to the catalog arrives here already drawn and there is no
second list to keep in step. */}
{category && (
<Surface
name={primitive.title}
api={primitive.api}
rail={category.id}
here={primitive.slug}
/>
)}
{/* Description */}
{primitive.description && (
<Text render="p" display="block" mt={32} maxW={768} fontSize="$3" lineHeight="1.4219rem" color={INK_80}>
{primitive.description}
</Text>
)}
{/* Features */}
{primitive.features && primitive.features.length > 0 && (
<Grid columns={{ min: 280, max: 2 }} gap={12} mt={40}>
{primitive.features.map((feature) => (
<XStack
key={feature}
items="flex-start"
gap="$3"
p="$4"
rounded="var(--radius-xl)"
borderWidth={1}
borderColor="$edge"
bg="$panel"
>
<View mt={2} shrink={0}>
<Check size={16} color="var(--foreground)" />
</View>
<Text fontSize="$2" lineHeight="1.15rem" color={INK_80}>
{feature}
</Text>
</XStack>
))}
</Grid>
)}
{/* Positioning line */}
<Text render="p" display="block" mt={56} fontSize="$2" lineHeight="1.15rem" color="var(--muted-foreground)">
{POSITIONING}
</Text>
{/* CTAs — anchors, not `Button asChild`.
`Button` is Tamagui, and a sub-theme makes it wrap its child in
`<span class="t_sub_theme" style="display:contents">`. That wrapper
needs the Tamagui provider context, which a SERVER component does not
carry: the server wrote a bare `<a role="button">` and the client
rendered the span, so every one of these pages failed hydration and
regenerated its whole tree. The sibling heroes never hit it because
both are 'use client'.
An anchor with the page's own tokens is what CloudLanding already
uses for exactly this row, so the shape is the house one — and it
drops a runtime from a page that only needed a few links.
The way in is the closing call's and the docs link is `Related`'s
below, which is why neither is here: one start action to a page, and
one docs link, from the map that says which product docs publishes. */}
<XStack mt="$5" flexWrap="wrap" gap="$3">
<Action href="/contact-sales">
Talk to us
</Action>
{primitive.github && (
<Action href={primitive.github} target="_blank" rel="noopener noreferrer">
<Github size={16} />
Source
</Action>
)}
</XStack>
</Box>
{/* The docs, and what sits beside this product. */}
{about && <Related product={about} />}
</Box>
)
}
export default CloudPrimitiveOverview