v0.19.0 frontend: /docs/* route tree + flyout nav + sessions browser
Reorganizes the /docs surface from a single DOCS.md route into a hub with a left-side flyout nav and three new sub-routes for the on-site sessions browser (roadmap item #30): /docs → redirect to /docs/user-guide /docs/user-guide → existing DOCS.md content /docs/sessions → redirect to /docs/sessions/about /docs/sessions/about → README.md of ohm-session-history /docs/sessions/<NNNN> → per-session transcript index /docs/sessions/<NNNN>/<f> → per-transcript view The flyout is a persistent left sidebar on desktop and a slide-out drawer on mobile (toggled by a ☰ button in the docs header). Its session list is driven by the /api/docs/sessions/manifest fetch — loading shows a skeleton; 502 shows an inline retry; empty manifest shows only the "About" row. Each sub-route owns its own empty-state / error handling: - 404 transcripts render "This transcript isn't published yet" with a link back to the parent session index, no JS crash. - 502 (gitea unreachable) renders a retry button. - Manifest 404 is mapped to {} server-side so the flyout renders cleanly with no error banner when no sessions are published yet. Analytics (per SPEC §21): - new EVENTS.DOC_VIEWED ("Doc Viewed") fires on each sub-route mount with `section`: 'user-guide' | 'sessions/about' | 'sessions/<NNNN>' | 'sessions/<NNNN>/<filename>'. - every interactive nav element carries aria-label + data-amp-track-name so autocapture rows are readable. The existing v0.14.0 Docs.jsx component is dropped — its content moved verbatim into DocsUserGuide.jsx; the new layout subsumes the back-button + signed-out home affordances it used to carry. All four new sub-routes reuse the existing MarkdownPreview renderer (marked + mermaid lazy-load) so no second markdown library lands. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
// DocsSessionTranscript.jsx — v0.19.0 / roadmap item #30.
|
||||
//
|
||||
// Per-transcript view at `/docs/sessions/:nnnn/:filename`. Fetches the
|
||||
// transcript body via the backend mediator and renders it through the
|
||||
// shared MarkdownPreview.
|
||||
//
|
||||
// Empty-state contract:
|
||||
// 404 → "This transcript isn't published yet" with a link back to
|
||||
// the parent session index
|
||||
// 502 → "Couldn't reach the session-history repo" + retry button
|
||||
|
||||
import { useEffect, useState, useCallback } from 'react'
|
||||
import { Link, useParams } from 'react-router-dom'
|
||||
import MarkdownPreview from './MarkdownPreview.jsx'
|
||||
import { getSessionTranscript } from '../api.js'
|
||||
import { EVENTS, track } from '../lib/analytics'
|
||||
|
||||
export default function DocsSessionTranscript() {
|
||||
const { nnnn, filename } = useParams()
|
||||
const [body, setBody] = useState('')
|
||||
const [status, setStatus] = useState('loading') // loading | ok | notfound | error
|
||||
const [reloadTick, setReloadTick] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
track(EVENTS.DOC_VIEWED, { section: `sessions/${nnnn}/${filename}` })
|
||||
}, [nnnn, filename])
|
||||
|
||||
useEffect(() => {
|
||||
let active = true
|
||||
setStatus('loading')
|
||||
getSessionTranscript(nnnn, filename)
|
||||
.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 }
|
||||
}, [nnnn, filename, reloadTick])
|
||||
|
||||
const retry = useCallback(() => setReloadTick(t => t + 1), [])
|
||||
|
||||
return (
|
||||
<article className="docs-article">
|
||||
<div className="docs-breadcrumbs">
|
||||
<Link
|
||||
to={`/docs/sessions/${nnnn}`}
|
||||
aria-label={`Back to session ${nnnn} index`}
|
||||
data-amp-track-name="Docs Transcript Back To Index"
|
||||
>
|
||||
← Session {nnnn}
|
||||
</Link>
|
||||
</div>
|
||||
{status === 'loading' && <p className="muted">Loading…</p>}
|
||||
{status === 'notfound' && (
|
||||
<div className="docs-empty">
|
||||
<p>This transcript isn't published yet.</p>
|
||||
<p>
|
||||
<Link
|
||||
to={`/docs/sessions/${nnnn}`}
|
||||
aria-label={`Back to session ${nnnn}`}
|
||||
data-amp-track-name="Docs Transcript Back To Session"
|
||||
>
|
||||
← Back to session {nnnn}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{status === 'error' && (
|
||||
<div className="docs-error" role="alert">
|
||||
<p>Couldn't reach the session-history repo.</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={retry}
|
||||
aria-label="Retry"
|
||||
data-amp-track-name="Docs Transcript Retry"
|
||||
>
|
||||
Try again
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{status === 'ok' && (
|
||||
<div className="philosophy-body">
|
||||
<MarkdownPreview content={body} />
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user