mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-30 14:58:08 +00:00
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.
102 lines
4.2 KiB
TypeScript
102 lines
4.2 KiB
TypeScript
import { notFound } from 'next/navigation'
|
|
import { Page, PageHero, Prose, Section } from '@/components/marketing/page-kit'
|
|
import { legalDoc } from '@/lib/legal'
|
|
import { pageMeta } from '@/lib/page-meta'
|
|
import { AUP_VERSION, TERMS_VERSION, versionDate } from '@hanzo/app/lib/terms'
|
|
|
|
/** The two documents a person agrees to at sign-up take their version from `@hanzo/app/lib/terms`, not their own front matter. */
|
|
const AGREED: Record<string, string> = { terms: TERMS_VERSION, 'acceptable-use': AUP_VERSION }
|
|
|
|
/**
|
|
* A legal document, rendered — the ONE place any of them is drawn.
|
|
*
|
|
* `content/legal/*.md` is the one authoritative version of each document and
|
|
* this is the one thing that renders it. Four routes reach it: `/legal/<slug>`
|
|
* for all ten, and `/privacy`, `/terms` and `/cookies` for the three whose
|
|
* short addresses were minted before the markdown stack existed.
|
|
*
|
|
* Those three used to be HAND-WRITTEN TSX, and they were not stale renderings
|
|
* of the same text — they were DIFFERENT DOCUMENTS. The TSX privacy policy ran
|
|
* thirteen generic sections against the markdown's nineteen, and the two
|
|
* disagreed about the minimum age and about whether research participation is
|
|
* on by default. A site cannot serve two answers to "what are your terms", so
|
|
* the bodies are gone and both addresses now read one file.
|
|
*
|
|
* The status line is not decoration. Every one of these is a DRAFT pending
|
|
* counsel, and the page says so above the text, from the document's own front
|
|
* matter. A reviewer relying on unexecuted terms because a page implied
|
|
* otherwise is the exact failure /trust exists to avoid.
|
|
*/
|
|
export function LegalDocument({ slug }: { slug: string }) {
|
|
const doc = legalDoc(slug)
|
|
if (!doc) notFound()
|
|
const effective = doc.status.toLowerCase() === 'effective'
|
|
const version = AGREED[slug] ?? doc.version
|
|
const date = AGREED[slug] ? versionDate(version) : doc.updated
|
|
return (
|
|
<Page>
|
|
<PageHero eyebrow="Legal" title={doc.title} lede={doc.summary} />
|
|
<Section>
|
|
<Prose>
|
|
{effective ? null : (
|
|
<p>
|
|
<strong>Draft.</strong> This document is not yet in force.
|
|
</p>
|
|
)}
|
|
<p>
|
|
Effective {date} · Version {version} ·{' '}
|
|
{doc.previous ? <a href={doc.previous}>Previous version</a> : 'Previous version: none, this is the first.'}
|
|
</p>
|
|
{doc.toc.length > 1 ? (
|
|
<nav aria-label="Contents">
|
|
<h2>Contents</h2>
|
|
<ul>
|
|
{doc.toc.map((row) => (
|
|
<li key={row.id}>
|
|
<a href={`#${row.id}`}>{row.text}</a>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</nav>
|
|
) : null}
|
|
</Prose>
|
|
</Section>
|
|
<Section>
|
|
<Prose html={doc.html} />
|
|
</Section>
|
|
</Page>
|
|
)
|
|
}
|
|
|
|
/**
|
|
* The metadata for a document served at its own short address, `/<slug>`.
|
|
*
|
|
* `/legal/<slug>` is the address, and this is what says so: the canonical
|
|
* points there, so search consolidates two URLs onto one document rather than
|
|
* ranking them against each other. `pageMeta` already carries that option and
|
|
* already states the reason — "left to point at itself it is a second, empty
|
|
* URL competing with the page that holds the words".
|
|
*
|
|
* A redirect stub was the alternative and it is not available here. This is a
|
|
* static export, so Next's `redirects()` emits nothing; the repo's substitute
|
|
* is a shell listed in `lib/publish`'s `EMPTY`, which makes the route
|
|
* `noindex` — and `shown()` in `components/home/shell.tsx` links a page in the
|
|
* chrome only while `policy()` calls it public. `@hanzogui/shell`'s footer
|
|
* registry points at `https://hanzo.ai/privacy` and `https://hanzo.ai/terms`,
|
|
* so a stub would delete both links from the footer of every page on the site,
|
|
* in a package this repo cannot edit.
|
|
*
|
|
* Title and description come from the document's own front matter, so nothing
|
|
* about it is typed twice.
|
|
*/
|
|
export function legalMeta(slug: string, path = `/${slug}`) {
|
|
const doc = legalDoc(slug)
|
|
if (!doc) throw new Error(`no legal document '${slug}' in content/legal`)
|
|
return pageMeta({
|
|
title: doc.title,
|
|
description: doc.summary,
|
|
path,
|
|
canonical: `/legal/${slug}`,
|
|
})
|
|
}
|