Release v0.20.0: /docs/specs surface + nested flyout nav + session body-list removal
Wave 9 follow-up to roadmap item #30 (Session 0017.0 shipped #30 as v0.19.0; v0.20.0 lands the operator-feedback follow-ups on top). 1. Specs on /docs/specs/<name> (backend docs_specs.py + frontend DocsSpec.jsx + DocsSpecsIndex.jsx). Configured via OHM_DOCS_SPECS; framework default carries OHM's two specs (rfc-app/SPEC.md + flotilla SPEC.md). Runtime fetch from gitea raw with 5-min TTL cache, mirroring docs_sessions.py. 2. Nested flyout nav hierarchy (DocsLayout.jsx). Sessions render as a tree with transcripts nested under each session row (labeled by .N ordinal). New Specs section between User Guide and Sessions. 3. /docs/sessions/<NNNN> body-list removed (DocsSessionIndex.jsx). Body becomes a session-overview card; navigation lives in the left nav. 19 new pytest cases for docs_specs (332 backend total green). Frontend build clean. Sync frontend/package-lock.json version drift (0.15.0 → 0.20.0) alongside the VERSION + package.json bump. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
// DocsSpecsIndex.jsx — v0.20.0.
|
||||
//
|
||||
// Landing route at `/docs/specs`. The operator-stated body shape for
|
||||
// the parallel `/docs/sessions/:nnnn` page is "no body list — pick a
|
||||
// transcript from the nav", and the same gesture applies here: the
|
||||
// `/docs/specs` route either redirects to the first configured spec
|
||||
// (the common case) or renders a "no specs configured" empty state
|
||||
// (only reachable if a deployment overrides `OHM_DOCS_SPECS` to an
|
||||
// empty list — the framework default has two entries).
|
||||
//
|
||||
// The redirect is client-side because the manifest is a single API
|
||||
// call away; server-side redirect would require either a backend
|
||||
// route for the bare `/docs/specs` path (out of scope for v0.20.0)
|
||||
// or a build-time bake of the first spec name (which couples the
|
||||
// frontend bundle to the deployment overlay, which we don't do).
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Navigate, Link } from 'react-router-dom'
|
||||
import { getSpecsManifest } from '../api.js'
|
||||
import { EVENTS, track } from '../lib/analytics'
|
||||
|
||||
export default function DocsSpecsIndex() {
|
||||
const [firstName, setFirstName] = useState(null)
|
||||
// Tri-state: 'loading' (waiting on manifest), 'redirect' (we have a
|
||||
// name to redirect to — render <Navigate>), 'empty' (no specs
|
||||
// configured), or 'error' (couldn't load the manifest at all).
|
||||
const [status, setStatus] = useState('loading')
|
||||
|
||||
useEffect(() => {
|
||||
track(EVENTS.DOC_VIEWED, { section: 'specs' })
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
let active = true
|
||||
getSpecsManifest()
|
||||
.then(payload => {
|
||||
if (!active) return
|
||||
const specs = (payload && payload.specs) || []
|
||||
if (specs.length === 0) {
|
||||
setStatus('empty')
|
||||
} else {
|
||||
setFirstName(specs[0].name)
|
||||
setStatus('redirect')
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (!active) return
|
||||
setStatus('error')
|
||||
})
|
||||
return () => { active = false }
|
||||
}, [])
|
||||
|
||||
if (status === 'redirect' && firstName) {
|
||||
return <Navigate to={firstName} replace />
|
||||
}
|
||||
|
||||
return (
|
||||
<article className="docs-article">
|
||||
<h1 className="docs-article-title">Specs</h1>
|
||||
{status === 'loading' && <p className="muted">Loading…</p>}
|
||||
{status === 'empty' && (
|
||||
<div className="docs-empty">
|
||||
<p>No specs are configured for this deployment.</p>
|
||||
<p>
|
||||
<Link
|
||||
to="/docs/user-guide"
|
||||
aria-label="User guide"
|
||||
data-amp-track-name="Docs Specs Empty User Guide Link"
|
||||
>
|
||||
← Back to the user guide
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{status === 'error' && (
|
||||
<div className="docs-error" role="alert">
|
||||
<p>Couldn't load the spec list.</p>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user