// DocsSpec.jsx — v0.20.0. // // Per-spec view at `/docs/specs/:name`. Fetches a configured spec // body via the backend mediator (which proxies the gitea raw URL) // and renders it through the shared MarkdownPreview. The page header // pulls the spec's `title` from the manifest so the breadcrumb-free // page still names what you're looking at. // // Empty-state contract: // 404 → "This spec isn't published yet / unknown name" with a hint // to pick a configured spec from the nav. // 502 → "Couldn't reach the spec source" + retry button. // // History view is intentionally absent — the operator's framing for // v0.20.0 is "current version only; git is the history surface". // A small "View on gitea" link beside the title points at the // upstream source URL the manifest carries so the history gesture // remains one click away. import { useEffect, useState, useCallback } from 'react' import { Link, useParams } from 'react-router-dom' import MarkdownPreview from './MarkdownPreview.jsx' import { getSpec, getSpecsManifest } from '../api.js' import { EVENTS, track } from '../lib/analytics' export default function DocsSpec() { const { name } = useParams() const [title, setTitle] = useState('') const [sourceUrl, setSourceUrl] = useState('') const [body, setBody] = useState('') const [status, setStatus] = useState('loading') // loading | ok | notfound | error const [reloadTick, setReloadTick] = useState(0) useEffect(() => { track(EVENTS.DOC_VIEWED, { section: `specs/${name}` }) }, [name]) // Title + upstream URL from the manifest (cheap — the manifest is // derived from an env var on the backend, no network). useEffect(() => { let active = true getSpecsManifest() .then(payload => { if (!active) return const entry = (payload && payload.specs || []).find(s => s.name === name) setTitle((entry && entry.title) || '') setSourceUrl((entry && entry.url) || '') }) .catch(() => { // Title + source link are decorative; the body fetch below // is the load-bearing one. A failed manifest fetch just // leaves the page rendering the bare name. }) return () => { active = false } }, [name]) useEffect(() => { let active = true setStatus('loading') getSpec(name) .then(text => { if (!active) return setBody(text || '') setStatus('ok') }) .catch(e => { if (!active) return if (e.status === 404) { setStatus('notfound') } else { setStatus('error') } }) return () => { active = false } }, [name, reloadTick]) const retry = useCallback(() => setReloadTick(t => t + 1), []) const header = title || `Spec: ${name}` return (

{header}

{sourceUrl && ( View source )}
{status === 'loading' &&

Loading…

} {status === 'notfound' && (

This spec isn't available.

← Back to the user guide

)} {status === 'error' && (

Couldn't reach the spec source.

)} {status === 'ok' && (
)}
) }