mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
ProductFooter stacked "Open source" with its pills, then "Get <name>" with its own tagline and buttons, each in a band of its own, so the 68 pages that end on it showed three headings in a row counting the closing call. It is one band now: Get <name>, the product's line, the licence and repository pills under them, and Deploy to Cloud and Self-host beside them from 768px. The parts keep the test ids the products audit reads. DeployBlock no longer falls back to "Deploy in seconds", a claim e2e/unit/cards.spec.ts keeps off the share cards for the same reason.
82 lines
2.7 KiB
TypeScript
82 lines
2.7 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* Deploy block.
|
|
*
|
|
* Primary CTA → platform.hanzo.ai/deploy/<slug> (managed cloud deploy).
|
|
* Secondary CTA → docs.hanzo.ai/docs/<docs_slug> (self-host docs), shown only
|
|
* when docs actually publishes a page for the product — `docsUrl` returns null
|
|
* otherwise, and a Self-host button that lands on a 404 is worse than no
|
|
* button.
|
|
*
|
|
* Both URLs derive from products-metadata so there is exactly one mapping
|
|
* from slug to deploy URL and one from slug to docs URL.
|
|
*
|
|
* The two controls are `Action`, this site's one button shape in its two
|
|
* weights. They had each written that shape out as ten utilities, and the
|
|
* filled one reached for `bg-primary`, which is a token gui redeclares as a
|
|
* self-reference — the rule states `--pure-white` and cannot be poisoned that
|
|
* way.
|
|
*
|
|
* The name lost its `hz-` prefix when this document's rules did, and this call
|
|
* site did not follow — so "Deploy to Cloud", the primary action of ~78 product
|
|
* pages, was shipping unfilled and unoutlined. `Start.tsx` carries the
|
|
* measurement.
|
|
*/
|
|
|
|
import React from 'react'
|
|
import { Rocket, BookOpen, ArrowRight } from 'lucide-react'
|
|
import { deployUrl, docsUrl } from '@/lib/constants/products-metadata'
|
|
import { YStack } from '@hanzo/ui'
|
|
import { Action } from '@hanzo/ui/marketing'
|
|
|
|
type Props = {
|
|
slug: string
|
|
className?: string
|
|
}
|
|
|
|
/** Below `sm` the pair is a column and each control stretches, so a label with
|
|
* nothing centring it sits at the left edge of a full-width button. */
|
|
const CENTRED = { justify: "center" } as const
|
|
|
|
export function DeployBlock({ slug, className = '' }: Props) {
|
|
const deploy = deployUrl(slug)
|
|
const docs = docsUrl(slug)
|
|
|
|
return (
|
|
// A plain element carries the test id the products audit locates this
|
|
// block by; a data attribute handed to a gui stack is not something this
|
|
// repo has measured reaching the DOM.
|
|
<div data-testid="deploy-block" className={className || undefined}>
|
|
<YStack gap={12} justify="center" $sm={{ flexDirection: 'row' }}>
|
|
<Action
|
|
data-testid="deploy-cta"
|
|
href={deploy}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
fill
|
|
{...CENTRED}
|
|
>
|
|
<Rocket width={16} height={16} />
|
|
Deploy to Cloud
|
|
<ArrowRight width={16} height={16} />
|
|
</Action>
|
|
{docs && (
|
|
<Action
|
|
data-testid="self-host-cta"
|
|
href={docs}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
{...CENTRED}
|
|
>
|
|
<BookOpen width={16} height={16} />
|
|
Self-host
|
|
</Action>
|
|
)}
|
|
</YStack>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default DeployBlock
|